home *** CD-ROM | disk | FTP | other *** search
- From chris@chris.telip.uni-sb.de Wed Mar 1 17:57:18 1995
- From: chris@chris.telip.uni-sb.de (Chris Blum)
- Newsgroups: comp.sys.ibm.pc.hardware.comm,comp.os.msdos.programmer
- Subject: The Serial Port rel. 18, part 1/3
- Date: 23 Feb 1995 00:22:09 +0100
- Organization: The Outside of the Asylum
- Reply-To: chris@phil.uni-sb.de (Chris Blum)
- NNTP-Posting-Host: chris.telip.uni-sb.de
- Keywords: comm, COM, UART, serial port
-
- Date of release: 22 Feb 1995 Release 18
-
- This is a summary on serial communication using the TTY protocol. It
- contains information on the TTY protocol and hardware and software implemen-
- tations for IBM PCs which has been derived from National Semiconductor data
- sheets and practical experience of the author and his supporters. Starting
- with release 5, some information on modems has been added.
-
- If you want to contribute to this file in any way, please email me (probably
- just reply to this posting). My email address is: chbl@stud.uni-sb.de or
- chris@phil.uni-sb.de. See the end for details.
-
- It's the seventeenth publication of this file. Some errors have been corrected
- and some information has been added (which has surely brought other errors
- with it, see Murphy's Law).
-
- [] brackets often indicate comments to sneaked material; copied lines are
- indented. I've made great efforts to always mention who's to be credited.
- Please tell me if you find something that you've written that's not correctly
- associated with your name.
-
- This compilation of information is (C) Copyright 1993 - 1995 by Christian
- Blum; all rights reserved. This file is not to be reproduced commercially,
- not even partially, without written permission. You are allowed to use it
- in any other way you like. I don't want any (monetary) profit being drawn
- out of it (neither by me nor by others! I don't mind if you have a look
- or two at it at work though... :-). Please feel free to provide this file
- to others for free or at your own expenses.
-
-
- Changes since the last publication
- ==================================
-
- Used more proper interrupt acknowledging in the examples (namely the
- method I suggested some chapters before :) in order to avoid lock-ups
- on MCA computers. (Thanks, Erik)
-
- Added some info on the auto flow-control feature of the TL16C550C.
- (Thanks, naddy)
-
-
- What I'm doing
- ==============
-
- I'm no longer very much into DOS (though I still make some money with it :),
- so don't expect me reading all the groups regularly that I'm posting this
- to.
-
-
- What others are doing
- =====================
-
- There is a file available from ftp.phil.uni-sb.de, pub/staff/chris called
- The_Serial_Port.more05 that contains an article by Bob Niland covering
- serial communication under Windows. It is regularly posted to
- comp.sys.ibm.pc.hardware.comm and other groups; if you obtain it from
- there it's probably more up to date.
-
-
- No more "Automatic File Delivery" (AFD) service
- ===============================================
-
- The automatic mail reply service I've mentioned in earlier releases of
- this file is still available, but I won't do anything to keep it
- alive from now on. Please obtain the files via anonymous ftp from
- ftp.phil.uni-sb.de (134.96.80.220), pub/staff/chris.
-
- If you don't have access to ftp, try using an ftp to email gateway
- (ftpmail@decwrl.dec.com or bitftp@pucc.princeton.edu, and probably
- a lot more; put 'help' in the body to obtain instructions). If everything
- fails, write to me.
-
-
- Acknowledgements (quite a bunch of people by now...)
- ================
-
- The following persons have contributed (directly or indirectly :-) to this
- summary by providing information or making suggestions/reporting errors.
- Tell me if your name is missing.
-
- Madis Kaal <mast@anubis.kbfi.ee>
- Steve Poulsen <stevep@ims.com>
- Scott C. Sadow <NS16550A@mycro.UUCP>
- Dan Norstedt <?>
- Alan J. Brumbaugh <brumba@maize.rtsg.mot.com>
- Mike Surikov <surikov@adonis.iasnet.com>
- Varol Kaptan <E66964%trmetu.bitnet@relay.EU.net>
- Richard F. Drushel <rfd@po.CWRU.Edu>
- John A. Limpert <johnl@n3dmc.svr.md.us>
- Brent Beach <ub359@freenet.victoria.bc.ca>
- Torbjoern (sp?) Lindgren <tl@etek.chalmers.se>
- Stephen Warner <ee_d316@dcs.kingston.ac.uk>
- Kristian Koehntopp <kris@black.toppoint.de>
- Angelo Haritsis <ah@doc.ic.ac.uk>
- Jim Graham <jim@n5ial.mythical.com>
- Ralf Brown <ralf@cs.cmu.edu>
- Alfred Arnold <zam036@zam112.zam.kfa-juelich.de>
- Andrew M. Langmead <aml@world.std.com>
- Richard Clayton <richard@locomotive.com>
- Christof Baumgaertner <baumg@rhrk.uni-kl.de>
- Goran Bostrom <GORAN@infovox.se>
- Brian Mork <bmork@opus-ovh.spk.wa.us>
- Richard Steven Walz <rstevew@armory.com>
- Scott David Daniels <daniels@cse.ogi.edu>
- Brian Onn <Brian.Onn@Canada.Sun.COM>
- Erik Suurmaa <erik@lerdeil.ee>
- Terence Edwards <Terence@tedwards.demon.co.uk>
- Christian 'naddy' Weisgerber <naddy@mips.pfalz.de>
-
-
-
- Introduction
- ============
-
- One of the most universal parts of the PC (except for the CPU, of course :-)
- is its serial port. You can connect a mouse, a modem, a printer, a plotter,
- another PC, dongles :) ...
-
- But its usage (both software and hardware) is one of the best-kept secrets
- for most users, besides that it is not difficult to understand how to
- connect (not plug in) devices to it and how to program it.
-
- Regard this file as a manual for the serial port of your PC for both
- hardware and software.
-
-
- Historical summary
- ------------------
-
- In early days of telecommunication, errand-boys and optical signals (flags,
- lights, clouds of smoke) were the only methods of transmitting information
- across long distances. With increasing requirements on speed and growing
- amount of information, more practical methods were developed. One milestone
- was the first wire-bound transmission on May 24th, 1844 ("What hath God
- wrought", using the famous Morse alphabet). Well, technology improved a bit,
- and soon there were machines that could be used like typewriters, except that
- you typed not only on your own sheet of paper but also on somebody elses.
- The only thing that has changed on the step from the teletype to your PC
- regarding serial communications is speed.
-
-
- The TTY (teletyping) protocol
- -----------------------------
-
- Definition: A protocol is a clear description of the LOGICAL method of
- transmitting information. This does NOT include physical realization.
-
- There is a difference between bits per second and baud (named after J. M. E.
- Baudot, one of those guys who gave a real push to teletyping): 'baud' means
- 'state changes of the line per second' while 'bits per second' ...
- well, bits per second means bits per second. You may find this a bit weird
- because the numbers are often the same; there's only a difference if the
- line has more than two states. Since this is not the case with the RS-232C
- (EIA-232) port of your PC, most people don't differentiate between 'baud' and
- 'bits per second', while I do. For your convenience, I've replaced baud with
- bps even in copied material without special notice. Where you still find baud,
- it should read bps in most cases (I didn't change labels in source codes, pin
- names in data sheet information etc.). To illustrate the difference I give you
- some figures: 2400 bps at 8n1 carry 1920 bits of information per second, and
- modems send them at 600 baud thru' the phone wires using eight line states,
- while 1200 bps at 7e1 carry 840 bits of information per second that modems
- send at 600 baud using four different line states. I know it's confusing...
- that's why I quote this from a letter I received from Brent Beach. He explained
- it more clearly than I did (I've added some information):
-
- Perhaps a small diagram might help, showing the relationship among the
- players:
-
- [bps] [baud]
- CPU Data Serial Phone
- Bus -- bytes --> Port -- bits --> Modem -- tones --> line --
- |
- |
- CPU Data Serial |
- Bus <-- bytes -- Port <-- bits -- Modem <-- tones ----------
- (1) (2) (3)
-
- The serial port accepts bytes from the CPU data bus and passes bits to the
- modem. In doing this, the serial port can add or delete bits, depending on
- the coding scheme in use.
-
- At (1) we are concerned with bytes per second. At (2) we are concerned with
- bits per second, and at (3) it's baud. We distinguish because the number of
- bits at (2) need not be equal to the number of bits (that is, bytes times 8)
- at (1), and the number of state changes at (3) is not necessarily the same
- as the number of bits before.
- Bits can be stripped going from (1) to (2): the serial port may transmit
- only 6 or 7 of the 8 bits in the byte. Bits can be added going from (1) to
- (2): the serial port can add a parity bit and stop bits. From (2) to (3),
- bits may be clustered to groups that are transmitted using different
- encoding schemes like 'Frequency Shift Keying' or 'Quadrature Amplitude
- Modulation', to name some.
-
- You can determine the transfer rate in bytes per second depending on the
- serial port speed and the coding system. For example,
-
- 8n1: 1 start bit + 8 data bits + 1 stop bit = 10 bits per word.
- At 2400 bps, this is 240 bytes/characters per second. 2400 bps are
- normally transmitted using QAM ('Quadrature Amplitude Modulation')
- where 4 bits are clustered, and hence encoded to 600 baud.
-
- 7e1: 1 start bit, 7 data bits, 1 even parity bit, 1 stop bit = 10 bits
- per word. At 1200 bps, this is 120 bytes/characters per second. 1200
- bps are encoded using DPSK ('Differential Phase Shift Keying', two
- bits are clustered), and this results again in 600 baud.
-
-
- Now let's leave modems for a while and have a look at the serial port itself.
-
- The TTY protocol uses two different line states called 'mark' and 'space'.
- (For the sake of clearness I name the line states 'high' (voltage) for
- positive and 'low' (voltage) for negative voltages). If no data is
- transmitted, the line is in its quiescent 'low' ('mark') state or in the
- 'break' state ('high'). Data looks like
-
- space +---+ +---+ +---+ high '0' +12V
- | | | | | |
- mark ----------+ +-------+ +---+ +------- low '1' -12V
-
- (1) --------(2)-------- (3)
-
- (1) start bit (2) data bits (3) stop bit(s)
-
- Steve Walz reported that in most (all?) books these kind of diagrams are drewn
- the other way round (I just copied what I saw on the oscilloscope) and
- that he'd use the labels 'high' and 'low' the other way round, corresponding
- to the signals on the TTL level (a matter of taste I guess); here is what he
- told me:
-
- In American texts, we will expect to see the data frame for serial transfer
- of all kinds represented, despite the method of transfer (RS-232C, RS-422,
- and optical even), as being an interruption of a normally HI state, and we
- expect to see the diagram you drew in the older release 8, but with the
- labelling corrected as I have indicated:
-
- mark ----------+ +-------+ +---+ +------- high '1' -12V
- logical 1 | S | 1 1 | 0 | 1 | 0 | Stop
- space +---+ +---+ +---+ low '0' +12V
- (1) --------(2)---------(3)
- (1) start bit (2) data bits (3) stop bit(s)
- Thus transmitting the bit stream 01011, which is LSB first, MSB last.
-
- Indeed it seems to us that a zero SHOULD be the quiescent state, and the
- one an active state, but the first teletypes used a current loop to
- continuously monitor the state of the line, and thus current flow was
- regarded as a 1 and it is "MARK" -ing time, and a signal then left a "SPACE"
- in the graph of current flow designating a zero. Thus the bits following
- the start bit at level zero were true to their bit values, and a 11111 in
- 5 bit baudot looked like this, using three dashes per bit:
-
- mark ------ ------------------------ 1 HI +5V TTL -12V RS-232C
- space --- 0 LO 0V TTL +12V RS-232C
- s 1 1 1 1 1 stop
-
- and the baudot 10101 would appear thus:
-
- mark ------ --- --- ------------ 1 HI +5V TTL -12V RS-232C
- space --- --- --- 0 LO 0V TTL +12V RS-232C
- s 1 0 1 0 1 stop
-
- and the baudot 01010 would appear thus:
-
- mark ------ --- --- --------- 1 HI +5V TTL -12V RS-232C
- space ------ --- --- 0 LO 0V TTL +12V RS-232C
- s 0 1 0 1 0 stop
-
- and finally baudot 00000 would appear:
-
- mark ------ --------- 1 HI +5V TTL -12V RS-232C
- space ------------------ 0 LO 0V TTL +12V RS-232C
- s 0 0 0 0 0 stop
-
- Now I know that we don't send five bit baudot over RS-232C now, but I
- wasn't about to try 8 bits, if you don't mind! :)
-
- I know that people get confused about the proper way to draw these, since
- we use inverted voltages to send them via RS-232C interface now, but they
- are still called logical "1" and "mark" when it is really -12 Volts DC, and
- it is called "0" and "space" when it is +12 Volts. And logical one or "mark"
- corresponds to +5 Volts, while logical zero is "space" and corresponds to 0
- Volts. It is this way both within the parallel bus of the computer or the
- transmit output of a UART/USART, with the exception that this data frame is
- terminated by remaining logic "1" or "mark" as a stop bit and preface
- to the next data frame.
-
- Both transmitter (TX) and receiver (RX) use the same data rate (measured
- in bps, see above), which is the reciprocal value of the smallest time
- interval between two changes of the line state. TX and RX know about the
- number of data bits (probably with a parity bit added), and both know about
- the (minimum!) size of the stop step (called the stop bit or the stop bits,
- depending on the size of the stop step; normally 1, 1.5 or 2 times the size
- of a data bit). Data is transmitted bit-synchronously and word-asynchronously,
- which means that the size of the bits, the length of the words etc.pp. is
- clearly defined while the time between two words is undefined.
-
- The start bit indicates the beginning of a new data word (this means one
- single character). It is used to synchronize transmitter and receiver and
- is always a logical '0' (so the line goes 'high' or 'space').
-
- Data is transmitted LSB to MSB, which means that the least significant
- bit (LSB, Bit 0) is transmitted first with 4 to 7 bits of data following,
- resulting in 5 to 8 bits of data. A logical '0' is transmitted by the
- 'space' state of the line (+12V), a logical '1' by 'mark' (-12V).
-
- A parity bit can be added to the data bits to allow error detection.
- There are two (well, actually five) kinds of parity: odd and even (plus
- none, mark and space). Odd parity means that the number of 'low' or 'mark'
- steps in the data word (including an optional parity bit, but not the
- framing bits) is always odd, so the parity bit is set accordingly (I don't
- have to explain 'even' parity, must I?). It is also possible to set the
- parity bit to a fixed state or to omit it. See Registers section for
- details on types of parity.
-
- The stop bit does not indicate the end of the word (as it could be derived
- >from its name); it rather separates two consecutive words by putting the
- line into the quiescent state for a minimum time (that means the stop bit
- is a logical '1' or 'mark') in order for the next start bit to be clearly
- visible.
-
- The framing protocol is usually described by a sequence of numbers and
- letters, eg. 8n1 means 1 start bit (always the same, thus omitted), 8 bits
- of data, no parity bit, 1 stop bit. 7e2 would indicate 7 bits of data,
- even parity, 2 stop bits (but I've never seen this one...). The usual thing
- is 8n1 or 7e1.
-
- Your PC is capable of serial transmission at up to 115,200 bps (step size
- of 8.68 microseconds!). Typical rates are 300 bps, 1200 bps, 2400 bps and
- 9600 bps, with 19200 bps, 38400 bps and 57600 bps becoming more and more
- popular with high speed modems. Note that some serial ports have difficulties
- with high speeds! I've seen PS/2's failing to operate at more than 38400 bps!
- How come that IBM machines are often the least IBM compatible? :-)
-
- This is what John A. Limpert told me about teletypes:
-
- Real (mechanical) teletypes used 1 start bit, 5 data bits and 1.42 stop
- bits. Support for 1.5 stop bits in UARTs was a compromise to make the
- UART timing simpler. Normal speeds were 60 WPM (word per minute),
- 66 WPM, 75 WPM and 100 WPM. A word was defined as 6.1 characters.
- The odd stop bit size was a result of the mechanical nature of the
- machine. It was the time that the printer needed to finish the current
- character and get ready for the next character. Most teletypes used
- a 60 mA loop with a 130 V battery. 20 mA loops and lower battery voltages
- became common when 8 level ASCII teletypes were introduced. The typical
- ASCII teletype ran at 110 bps with 2 stop bits (11 bits per character).
-
- It's surely more exact than what I wrote in previous releases. I've just got
- to add that at least in Germany 50 bps was a familiar speed. And I think the
- lower battery voltage he's talking about was 24 volts.
-
-
- The physical transmission
- -------------------------
-
- Teletypes used a closed-loop line with a quiescent current of 20ma and a
- space current of 0ma (typically), which allows to detect a 'broken line'
- (hence the name of the 'break' flag, see the Registers section). The RS-232C
- port of your PC uses voltages rather than currents to indicate logical states:
- 'mark'/'low' is signaled by -3v to -15v (typically -12V) and represents a
- logical '1', 'space'/'high' is signaled by +3v to +15v (typically +12V) and
- represents a logical '0'. The typical output impedance of the serial port of
- a PC is 2 kiloohms (resulting in about 5ma @ 10v), the typical input impedance
- is about 4.3 kiloohms, so there should be a maximum fan-out of 5 (5 inputs can
- be connected to 1 output). Please don't rely on this, it may differ from PC
- to PC.
-
- Three lines (RX, TX & ground) are at least needed to make up a bidirectional
- connection.
-
- Q. Why does my PC have a 25pin/9pin connector if there are only 3 lines
- needed?
- A. There are several status lines that are only used with modems etc. See the
- Hardware section and the Registers section of this file.
-
- Q. How can I easily connect two PCs by a three-wire lead?
- A. Connect RX1 to TX2 and vice versa, GND1 to GND2. In addition to this,
- connect RTS to CTS & DCD and connect DTR to DSR at each end (modem software
- often relies on that). See the hardware section for further details.
-
- Please be aware that at 115,200 bps (ie. ca. 115 kHz, but we need the
- harmonics up to at least 806 kHz) lines can no longer be regarded as 'ideal'
- transmission lines. They are low-pass filters and tend to reflect and mutilate
- the signals, but some ten meters of twisted wire should always be OK (I use 3m
- of screened audio cable for file transfer purposes, and it works fine. Not
- that other kinds of wire wouldn't do; I took what I found). See a good book on
- transmission lines if you're interested in why long lines can be a problem.
-
- This has been posted to comp.os.msdos.programmer by Andrew M. Langmead:
-
- The RS-232C spec. has an official limit of 50 ft for RS-232C cables.
- Realistically they can be much longer. The book "Managing UUCP and
- Usenet" by O'Reilly and Associates has a table that they credit to
- "Technical Aspects of Data Communications", by McNamara (Digital
- Press, 1992). It lists the maximum distances for an RS-232C
- connection.
-
- Baud Rate | max distance | max distance
- | shielded cable | unshielded cable
- ----------------------------------------------------------
- 110 | 5000ft | 3000ft
- 300 | 5000ft | 3000ft
- 1200 | 3000ft | 3000ft
- 2400 | 1000ft | 500ft
- 4800 | 1000ft | 250ft
- 9600 | 250ft | 250ft
-
- Please note that "baud" is correct in this case, because we're speaking of
- the transmission line itself.
-
- This is what Torbjoern (sp?) Lindgren told me:
-
- I have successfully transmitted at 115,200 with over 30m long cables!
- And it wasn't especially good wires. I had some old telecables with 20
- individual wires, and used 7 of them for transfer, and left the others
- unconnected.
-
- I don't remember the exact length, but I know it was something over
- 30m, and it probably was closer to 40m than 30m. The unused lines
- probably shielded the lines from each other or something like that.
- The computers used were two PC-compatibles with off-the-shelf
- com-ports. Nothing fancy.
-
- Note that some serial ports are more critical with mutilated signals than
- others, so you just have to try and find out yourself what works.
-
-
-
- Hardware
- ========
-
-
- The connectors
- --------------
-
- PCs have 9pin/25pin male SUB-D connectors. The pin layout is as follows
- (seen from outside your PC):
-
- 1 13 1 5
- _______________________________ _______________
- \ . . . . . . . . . . . . . / \ . . . . . /
- \ . . . . . . . . . . . . / \ . . . . /
- --------------------------- -----------
- 14 25 6 9
-
- Name (V24) 25pin 9pin Dir Full name Remarks
- --------------------------------------------------------------------------
- TxD 2 3 o Transmit Data Data
- RxD 3 2 i Receive Data Data
- RTS 4 7 o Request To Send Handshaking
- CTS 5 8 i Clear To Send Handshaking
- DTR 20 4 o Data Terminal Ready Status
- DSR 6 6 i Data Set Ready Status
- RI 22 9 i Ring Indicator Status
- DCD 8 1 i Data Carrier Detect Status
- GND 7 5 - Signal ground Reference level
- - 1 - - Protective ground Don't use this one
- as signal ground!
-
- The most important lines are RxD, TxD, and GND. Others are used with
- modems, printers and plotters to indicate internal states.
-
- '1' ('mark', 'low') means -3v to -15v, '0' ('space', 'high') means +3v
- to +15v. On status lines, 'high' is the active state: status lines go to the
- positive voltage level to signal events.
-
- The lines are:
-
- RxD, TxD: These lines carry the data; 1 is transmitted as 'mark' (what I
- call 'low') and 0 is transmitted as 'space' ('high').
-
- RTS, CTS: Are used by the PC and the modem/printer/whatsoever (further
- on referred to as the data set, or DCE) to start/stop a communication.
- The PC sets RTS to 'high', and the data set responds with CTS 'high'.
- (always in this order). If the data set wants to stop/interrupt the
- communication (eg. imminent buffer overflow), it drops CTS to 'low';
- the PC uses RTS to control the data flow.
-
- DTR, DSR: Are used to establish a connection at the very beginning, ie.
- the PC and the data set 'shake hands' first to assure they are both
- present. The PC sets DTR to 'high', and the data set answers with DSR
- 'high'. Modems often indicate hang-up by resetting DSR to 'low' (and
- sometimes are hung up by dropping DTR).
-
- (These six lines plus GND are often referred to as '7 wire'-connection or
- 'hand shake'-connection.)
-
- DCD: The modem uses this line to indicate that it has detected the
- carrier of the modem on the other side of the phone line. The signal is
- rarely used by the software.
-
- RI: The modem uses this line to signal that 'the phone rings' (even if
- there is neither a bell fitted to your modem nor a phone connected :-).
-
- GND: The 'signal ground', ie. the reference level for all signals.
-
- Protective ground: This line is connected to the power ground of the
- serial adapter. It should not be used as a signal ground, and it
- MUST NOT be connected to GND (even if your DMM [Digital MultiMeter] shows
- up an ohmic connection!). Connect this line to the screen of the lead (if
- there is one). Connecting protective ground on both sides makes sure that
- no large currents flow thru' GND in case of an insulation defect on one
- side (hence the name).
-
- Technical data (typical values for PCs):
-
- Signal level: -10.5v/+11v
- Short circuit current: 6.8ma
- Output impedance: ca 2 kiloohms (non-linear!)
- Input impedance: ca 4.3 kiloohms (non-linear!)
-
-
- Other asynchronous hardware than RS-232C
- ----------------------------------------
-
- There are several other standards that use the same chipset and protocol as
- RS-232C. RS-422 and the more robust (but compatible) version RS-485 (to name
- some) use two wires for every signal. The transmitters can usually be
- disabled and enabled by software, which makes it possible to use such
- equipment in a bus system (RX and TX part share the same lines). Despite
- >from the possibility to enable and disable the receiver/transmitter section
- of the port, they are fully compatible to existing RS-232C software if a
- compatible chipset is used.
-
- It's not possible to connect eg. RS-232C to RS-485 without an appropriate
- interface.
-
-
- Connecting devices (or computers)
- ------------------
-
- When you connect a data set or DCE (eg. a modem), use this connection:
-
- GND1 to GND2
- RxD1 to RxD2
- TxD1 to TxD2
- DTR1 to DTR2
- DSR1 to DSR2
- RTS1 to RTS2
- CTS1 to CTS2
- RI1 to RI2
- DCD1 to DCD2
-
- In other words, simply connect each pin of the first plug with the
- corresponding pin of the other. This can easily be done using a
- 25-wire ribbon cable and two crimp connectors.
-
- When you connect another computer (or any other DTE, like a terminal), this
- is the wiring you need (it is called a "null modem" connection):
-
- GND1 to GND2
- RxD1 to TxD2
- TxD1 to RxD2
- DTR1 to DSR2
- DSR1 to DTR2
- RTS1 to CTS2
- CTS1 to RTS2
-
- If software wants it, connect DCD1 to CTS1 and DCD2 to CTS2.
-
- If hardware handshaking is not needed, you can omit the status lines.
- Connect:
-
- GND1 to GND2
- RxD1 to TxD2
- TxD1 to RxD2
-
- Additionally, connect (if software needs it):
-
- RTS1 to CTS1 & DCD1
- RTS2 to CTS2 & DCD2
- DTR1 to DSR1
- DTR2 to DSR2
-
- You won't need long wires for these! :-)
-
- Remember: the names DTR, DSR, CTS & RTS refer to the lines as seen from
- the DTE (your PC). This means that for your data set DTR & RTS are incoming
- signals and DSR & CTS are outputs! Modems, printers, plotters etc. are
- connected 1:1, ie. pin x to pin x.
-
-
- Base addresses & interrupts
- ---------------------------
-
- Normally, the following list is correct for your PC; note however that
- if the BIOS can't find a port, it won't leave spaces in its port
- table, so if there is no UART at 0x3E8, the port at 0x2E8 will be
- called COM3 by DOS. Compare the section on logical vs. phyical names.
-
- Port Name Base address Int # Int level (IRQ)
-
- COM1 0x3F8 0xC 4
- COM2 0x2F8 0xB 3
- COM3 0x3E8 0xC 4
- COM4 0x2E8 0xB 3
-
- In your programs, you should refer to the table in the BIOS data segment.
- This is an excerpt from Ralf Brown's interrupt list (the actual author
- of this section is Robin Walker):
-
- Format of BIOS Data Segment at segment 40h:
- Offset Size Description
- 00h WORD Base I/O address of 1st serial I/O port, zero if none
- 02h WORD Base I/O address of 2nd serial I/O port, zero if none
- 04h WORD Base I/O address of 3rd serial I/O port, zero if none
- 06h WORD Base I/O address of 4th serial I/O port, zero if none
- Note: Above fields filled in turn by POST as it finds serial
- ports. POST never leaves gaps. DOS and BIOS serial device
- numbers may be redefined by re-assigning these fields.
-
- Please note that this table is not the bible and that the BIOS is not an
- evangelist (and I'm rather sceptical anyway :-). Your BIOS might not tell
- you the pure truth; if you get a zero it does not necessarily mean that
- there are no more serial ports available. Your programs should nevertheless
- have a look at the usual places for comm ports. See the "Programming" section
- for an example program that checks if a UART is installed at a given base
- address. Compare the "logical vs. physical names" section below.
-
- Another good idea is writing a small program that's then run in the
- AUTOEXEC.BAT and that fills the empty fields in the table with the
- correct values. My Award BIOS fails to recognize my fourth port at
- 0x2E8, so I typed a few bytes (14 altogether) in the debugger that
- write 0x2E8 to 0040:0006 and wrote them to a .COM file called in the
- AUTOEXEC.BAT.
-
- Also see the Programming section for a routine that detects the interrupt
- level/number that a UART uses. It's not a good idea to hard-code level
- 4 and 3; make it at least user configurable.
-
- See the chapter "Multi-Port Serial Adapters" for further information.
-
-
- Logical vs. physical ports
- --------------------------
-
- DOS users (like card manufacturers) tend to confuse logical and
- physical names. COM1, COM2, etc. are _logical_ names for the serial
- ports 0, 1, etc. found by the BIOS during POST (Power-On Self Test).
- The BIOS searches at four different I/O addresses for UARTS: 0x3F8,
- 0x2F8, 0x3E8, 0x2E8, in exactly this order. Every UART found has an
- entry in the comm port table at segment 0x40, offset 0. The BIOS
- manages up to four different UARTs, because the table has no more than
- four spaces. To make the confusion complete, Microsoft decided
- that DOS users wouldn't be comfortable with counting from zero, so
- they numbered the logical names of the comm ports from 1 to 4. Thus
- COM1 is the first UART found by the BIOS during POST, COM2 the second,
- and so on. Usually COM1 has 0x3F8 as base addresses, COM2 0x2F8 and so
- on, but that's not necessarily the case. Please do not use the logical
- DOS names when you really mean physical addresses. It is _not_
- possible to 'jumper a UART as COM3', at least not directly.
-
-
- The chipsets
- ------------
-
- In PCs, serial communication is realized with a set of three chips
- (there are no further components needed! (I know of the need of address
- logic & interrupt logic ;-) )): a UART (Universal Asynchronous
- Receiver/Transmitter) and two line drivers. Normally, the 82450/16450/8250
- does the 'brain work' while the 1488 and 1489 drive the lines (they are
- level shifting inverters; the 1488 drives the outputs).
-
- These chips are produced by many manufacturers; it's of no importance
- which letters are printed in front of the numbers (mostly NS for National
- Semiconductor). Don't regard the letters behind the number also (if it's not
- the 16550A or the 82C50A); they just indicate special features and packaging
- (Advanced, New, MILitary, bug fixes [see below] etc.) or classification.
- Letters in between the numbers (eg. 16C450) indicate technology (C=CMOS).
-
- You might have heard that it is possible to replace the 16450 by a 16550A
- to improve reliability and reduce software overhead. This is only useful if
- your software is able to use the FIFO (first in-first out) buffer feature.
- The chips are fully pin-compatible except for two pins that are not used by
- any serial adapter card known to the author: pin 24 (CSOUT, chip select out)
- and pin 29 (NC, no internal connection). With the 16550A, pin 24 is -TXRDY
- and pin 29 is -RXRDY, signals that aren't needed (except for DMA access -
- but not in the PC) and that even won't care if they are shorted to +5V or
- ground. Therefore it should always be possible to simply replace the 16450
- by the 16550A - even if it's not always useful due to lacking software
- capabilities. IT IS DEFINITELY NOT NECESSARY FOR COMMUNICATION AT UP TO LOUSY
- 9600 BPS! These rates can easily be handled by any CPU, and the
- interrupt-driven communication won't slow down the computer substantially. But
- if you want to use high-speed transfer with or without using the interrupt
- features (ie. by 'polling'), or multitasking, or multiple channels 'firing' at
- the same time, or disk I/O during transmission, it is recommendable to use the
- 16550A in order to make transmission more reliable if your software supports
- it (see excursion some pages below).
-
- There *are* differences between the 16550A, 16550AF, and 16550AFN. The 16550AF
- has one more timing parameter (t_RXI) specified that's concerned with the
- -RXRDY pin and that's of no importance in the PC. And the 16550AFN is the
- only one still believed to be free of bugs (see below). So the best choice for
- your PC is 16550AFN, but you are well off with the 16550AN, too. [Info from a
- posting of Jim Graham.]
-
- Don't worry about the missing 'A' if you have chips named xxx16550 which are
- not from National Semiconductor (eg. UM16550). As long as the first example
- in the 'Programming' section tells you that it is a 16550A, everything is
- fine. I've never heard of non-NS 16550s with the FIFO bug (see below).
-
-
- How to detect which chip is used
- --------------------------------
-
- This is really not difficult. The 8250 normally has no scratch register (see
- data sheet info below), the 16450/82450 has no FIFO, the 16550 has no working
- FIFO :-) and the 16550A performs alright. See the Programming section for
- an example program that detects which one is used in your PC.
-
- Note that there _are_ versions of the 8250 that _do_ have a scratch register!
- It's rather impossible to distinguish them from the 16450, but then it's not
- necessary either... I know of the SAB 82C50 from Siemens and the UM8250B
- (from UMC, a taiwanese company with a globe symbol; thanks, Alfred, for
- helping me out with that). You won't find 8250s in fast computers however,
- because their bus timing is too slow.
-
-
- Data sheet information
- ----------------------
-
- Some hardware information taken from the data sheet of National
- Semiconductor (shortened and commented):
-
- Pin description of the 16450 (16550A) [Dual-In-Line package]:
-
- +-----+ +-----+
- D0 -| 1 +-+ 40|- VCC
- D1 -| 2 39|- -RI
- D2 -| 3 38|- -DCD
- D3 -| 4 37|- -DSR
- D4 -| 5 36|- -CTS
- D5 -| 6 35|- MR
- D6 -| 7 34|- -OUT1
- D7 -| 8 33|- -DTR
- RCLK -| 9 32|- -RTS
- SIN -| 10 31|- -OUT2
- SOUT -| 11 30|- INTR
- CS0 -| 12 29|- NC (-RXRDY)
- CS1 -| 13 28|- A0
- -CS2 -| 14 27|- A1
- -BAUDOUT -| 15 26|- A2
- XIN -| 16 25|- -ADS
- XOUT -| 17 24|- CSOUT (-TXRDY)
- -WR -| 18 23|- DDIS
- WR -| 19 22|- RD
- VSS -| 20 21|- -RD
- +-------------+
-
- Note: The status signals are negated compared to the port! If you write a
- '1' to the appropriate register bit, the pin goes 'low' (to ground level).
- On its way to the port, the signal is inverted again; this means that the
- status line at the port goes 'high' if you write a '1'. The same is true
- for inputs: you get a '1' from the register bit if the line at the port is
- 'high'. SIN and SOUT are inverted, too. (negative voltage at the port
- means +5v at the UART).
-
- A0, A1, A2, Register Select, Pins 26-28:
- Address signals connected to these 3 inputs select a UART register for
- the CPU to read from or to write to during data transfer. A table of
- registers and their addresses is shown below. Note that the state of the
- Divisor Latch Access Bit (DLAB), which is the most significant bit of the
- Line Control Register, affects the selection of certain UART registers.
- The DLAB must be set high by the system software to access the Baud
- Generator Divisor Latches. [I'm sorry, but it's called that way even if it's
- a bps rate generator... :-)]. 'x' means don't care.
-
- DLAB A2 A1 A0 Register
- 0 0 0 0 Receive Buffer (read) Transmitter Holding Reg. (write)
- 0 0 0 1 Interrupt Enable
- x 0 1 0 Interrupt Identification (read)
- x 0 1 0 FIFO Control (write) [undefined with the 16450. CB]
- x 0 1 1 Line Control
- x 1 0 0 Modem Control
- x 1 0 1 Line Status
- x 1 1 0 Modem Status
- x 1 1 1 Scratch [special use on some boards. CB]
- 1 0 0 0 Divisor Latch (LSB)
- 1 0 0 1 Divisor Latch (MSB)
-
- -ADS, Address Strobe, Pin 25: The positive edge of an active Address
- Strobe (-ADS) signal latches the Register Select (A0, A1, A2) and Chip
- Select (CS0, CS1, -CS2) signals.
- Note: An active -ADS input is required when Register Select and Chip
- Select signals are not stable for the duration of a read or write
- operation. If not required, tie the -ADS input permanently low. [As it is
- done in your PC. CB]
-
- -BAUDOUT, Baud Out, Pin 15: This is the 16x clock signal from the
- transmitter section of the UART. The clock rate is equal to the main
- reference oscillator frequency divided by the specified divisor in the
- Baud Generator Divisor Latches. The -BAUDOUT may also be used for the
- receiver section by tying this output to the RCLK input of the chip. [Yep,
- that's true for your PC. CB].
-
- CS0, CS1, -CS2, Chip Select, Pins 12-14: When CS0 and CS1 are high and CS2
- is low, the chip is selected. This enables communication between the UART
- and the CPU.
-
- -CTS, Clear To Send, Pin 36: When low, this indicates that the modem or
- data set is ready to exchange data. This signal can be tested by reading
- bit 4 of the MSR. Bit 4 is the complement of this signal, and Bit 0 is '1'
- if -CTS has changed state since the previous reading (bit0=1 generates an
- interrupt if the modem status interrupt has been enabled).
-
- D0-D7, Data Bus, Pins 1-8: Connected to the data bus of the CPU.
-
- -DCD, Data Carrier Detect, Pin 38: blah blah blah, can be tested by
- reading bit 7 / bit 3 of the MSR. Same text as -CTS.
-
- DDIS, Driver Disable, Pin 23: This goes low whenever the CPU is reading
- data from the UART. It can be used to control bus arbitrary logic.
-
- -DSR, Data Set Ready, Pin 37: blah, blah, blah, bit 5 / bit 1 of MSR.
-
- -DTR, Data Terminal Ready, Pin 33: can be set active low by programming
- bit 0 of the MCR '1'. Loop mode operation holds this signal in its
- inactive state.
-
- INTR, Interrupt, Pin 30: goes high when an interrupt is requested by the
- UART. Reset low by the MR.
-
- MR, Master Reset, Pin 35: Schmitt Trigger input, resets internal registers
- to their initial values (see below).
-
- -OUT1, Out 1, Pin 34: user-designated output, can be set low by
- programming bit 2 of the MCR '1' and vice versa. Loop mode operation holds
- this signal inactive high. [Not used in the PC. CB]
-
- -OUT2, Out 2, Pin 31: blah blah blah, bit 3, see above. [Used in your PC to
- connect the UART to the interrupt line of the slot when '1'. CB]
-
- RCLK, Receiver Clock, Pin 9: This input is the 16x bps rate clock for
- the receiver section of the chip. [Normally connected to -BAUDOUT, as in
- your PC. CB]
-
- RD, -RD, Read, Pins 22 and 21: When RD is high *or* -RD is low while the
- chip is selected, the CPU can read data from the UART. [One of these is
- normally tied. CB]
-
- -RI, Ring Indicator, Pin 39: blah blah blah, Bit 6 / Bit 2 of the MSR.
- [Bit 2 only indicates change from active low to inactive high! Curious,
- isn't it? CB]
-
- -RTS, Request To Send, Pin 32: blah blah blah, see DTR (Bit 1).
-
- SIN, Serial Input, Pin 10.
-
- SOUT, Serial Output, Pin 11.
-
- -RXRDY, -TXRDY: refer to NS data sheet. These pins are used for DMA
- channeling. Since they are not connected in your PC, I won't describe them
- here.
-
- VCC, Pin 40, +5v
-
- VSS, Pin 20, GND
-
- WR, -WR: same as RD, -RD for writing data.
-
- XIN, XOUT, Pins 16 and 17: Connect a crystal here (1.5k betw. xtal & pin 17)
- and pin 16 with a capacitor of approx. 20p to GND and other xtal conn. 40p
- to GND. Resistor of approx. 1meg parallel to xtal. Or use pin 16 as an input
- and pin 17 as an output for an external clock signal of up to 8 MHz.
-
-
- Absolute Maximum Ratings:
-
- Temperature under bias: 0 C to +70 C
- Storage Temperature: -65 C to 150 C
- All input or output voltages with respect to VSS: -0.5v to +7.0v
- Power dissipation: 1W
-
- Further electrical characteristics see the very good data sheet of NS.
-
-
- UART Reset Configuration
-
- Register/Signal Reset Control Reset State
- --------------------------------------------------------------------
- IER MR 0000 0000
- IIR MR 0000 0001
- FCR MR 0000 0000
- LCR MR 0000 0000
- MCR MR 0000 0000
- LSR MR 0110 0000
- MSR MR xxxx 0000 (according to signals)
- SOUT MR high (neg. voltage at the port)
- INTR (RCVR errs) Read LSR/MR low
- INTR (data ready) Read RBR/MR low
- INTR (THRE) Rd IIR/Wr THR/MR low
- INTR (modem status) Read MSR/MR low
- -OUT2 MR high
- -RTS MR high
- -DTR MR high
- -OUT1 MR high
- RCVR FIFO MR/FCR1&FCR0/DFCR0 all bits low
- XMIT FIFO MR/FCR1&FCR0/DFCR0 all bits low
-
-
-
- Known problems with several chips
- ---------------------------------
-
- (From material Madis Kaal received from Dan Norstedt and stuff Erik Suurmaa
- sent me)
-
- 8250 and 8250-B:
-
- * These UARTs pulse the INT line after each interrupt cause has
- been serviced (which none of the others do). [Generates interrupt
- overhead. CB]
-
- * The start bit is about 1 us longer than it ought to be. [This
- shouldn't be a problem. CB]
-
- * 5 data bits and 1.5 stop bits doesn't work.
-
- * When a 1 is written to the bit 1 (Tx int enab) in the IER,
- a Tx interrupt is generated. This is an erroneous interrupt
- if the THRE bit is not set. [So don't set this bit as long as
- the THRE bit isn't set. CB]
-
- * The first valid Tx interrupt after the Tx interrupt is enabled
- is probably missed. Suggested workaround:
- 1) Wait for the THRE bit to become set.
- 2) Disable CPU interrupts. [?]
- 3) Write Tx interrupt enable to the IER.
- 4) Write Tx interrupt enable to the IER again.
- [Don't ask me why. I don't think it's necessary. CB]
- 5) Enable CPU interrupts. [?]
-
- * The TEMT (bit 6) doesn't work properly.
-
- * If both the Rx and Tx interrupts are enabled, and a Rx interrupt
- occurs, the IIR indication of the Tx interrupt may be lost.
- Suggested workarounds:
- 1) Test THRE bit in the Rx routine, and either set IER bit 1
- or call the Tx routine directly if it is set.
- 2) Test the THRE bit instead of using the IIR for Tx.
-
- [If one of these chips vegetates in your PC, go get your solder
- iron heated... CB]
-
- 8250A, 82C50A, 16450 and 16C450:
-
- * (Same problem as above:)
- If both the Rx and Tx interrupts are enabled, and a Rx interrupt
- occurs, the IIR indication may be lost; Suggested workarounds:
- 1) Test THRE bit in the Rx routine, and either set IER bit 1
- or call the Tx routine directly if it is set.
- 2) Test the THRE bit instead of using the IIR.
- 3) [Don't enable both interrupts at the same time. CB]
- 4) [Replace the chip by a 16550AFN; it has this bug fixed. CB]
-
- 16550 (without the A):
-
- * Rx FIFO bug: Sometimes the FIFO will get extra characters.
- [This seemed to be very embarrassing for NS; they've added a
- simple detection method for the 16550A (bit 6 of IIR). CB]
-
- 16550 AF
-
- * When the TX FIFO is enabled, a character loss can appear if
- the CPU writes a byte into the THR while the last one is still
- in the shift register (not completely sent). [This is documented
- by National Semiconductor; I've never experienced that, but that
- might be because I've never seen a 16550 AF :) CB]
-
- * Terence Edwards reports that his RS485 adapter with 16550 AF
- chips and a 16 MHz xtal gets parity bits wrong at 512 kbps; not
- very astonishing I'd say because the chip is only guaranteed to
- operate at 256kbps, with an 8 MHz xtal, and parity generators are
- rather slow circuits.
-
-
- No 16550 AFN bugs reported (yet?)
-
- [Same is true for the 16552, a two-in-one version of the 16550AFN, and the
- 16554, a quad-in-one version. CB]
-
- You might call this a bug, though: in FIFO mode, THRE (bit 5 or LSR) is
- cleared when there is at least one character in the Tx FIFO, not if the
- FIFO can't take any more bytes; that's rather absurd, but that's the way
- it is.
-
- A very solid method of handling the UART interrupts that avoids all possible
- int failures has been suggested by Richard Clayton, and I recommend it as
- well. Let your interrupt handler do the following:
- 1. Disarm the UART interrupts by masking them in the IMR of the ICU.
- 2. Send a specific or an unspecific EOI to the ICU (first slave, then
- master, if you're using channels above 7).
- 3. Enable CPU interrupts (STI) to allow high priority ints to be processed.
- 4. Read IIR and follow its contents until bit 0 is set.
- 5. Check if transmission is to be kicked (when XON received or if CTS
- goes high); if yes, call tx interrupt handler manually.
- 6. Disable CPU interrupts (CLI).
- 7. Rearm the UART interrupts by unmasking them in the IMR of the ICU.
- 8. Return from interrupt.
- This way you can arm all four UART ints at initialization time without
- having to worry about stuck interrupts. Start transmission by simply calling
- the tx interrupt handler after you've written characters to the tx fifo of
- your program.
-
- If you need details about programming the ICU, refer to Chris Hall's
- document about the 8259 that's available from my archive.
-
-
- [... continued ...]
-
- --
- Chris Blum <chris@phil.uni-sb.de> http://www.phil.uni-sb.de/~chris/
-
- From chris@chris.telip.uni-sb.de Wed Mar 1 17:57:22 1995
- From: chris@chris.telip.uni-sb.de (Chris Blum)
- Newsgroups: comp.sys.ibm.pc.hardware.comm,comp.os.msdos.programmer
- Subject: The Serial Port rel. 18, part 2/3
- Date: 23 Feb 1995 00:23:16 +0100
- Organization: The Outside of the Asylum
- Reply-To: chris@phil.uni-sb.de (Chris Blum)
- NNTP-Posting-Host: chris.telip.uni-sb.de
- Keywords: comm, COM, UART, serial port
-
- [... continued ...]
-
-
- Registers
- =========
-
- First some tables; full descriptions follow. Base addresses as specified by
- IBM for a full-blown system; compare the section on logical & physical names.
-
-
- 1st 2nd 3rd 4th Offs. DLAB Register
- ------------------------------------------------------------------------------
- 3F8h 2F8h 3E8h 2E8h +0 0 RBR Receive Buffer Register (read only) or
- THR Transmitter Holding Register (write only)
- 3F9h 2F9h 3E9h 2E9h +1 0 IER Interrupt Enable Register
- 3F8h 2F8h 3E8h 2E8h +0 1 DL Divisor Latch (LSB) These registers can
- 3F9h 2F9h 3E9h 2E9h +1 1 DL Divisor Latch (MSB) be accessed as word
- 3FAh 2FAh 3EAh 2EAh +2 x IIR Interrupt Identification Register (r/o) or
- FCR FIFO Control Register (w/o, 16550+ only)
- 3FBh 2FBh 3EBh 2EBh +3 x LCR Line Control Register
- 3FCh 2FCh 3ECh 2ECh +4 x MCR Modem Control Register
- 3FDh 2FDh 3EDh 2EDh +5 x LSR Line Status Register
- 3FEh 2FEh 3EEh 2EEh +6 x MSR Modem Status Register
- 3FFh 2FFh 3EFh 2EFh +7 x SCR Scratch Register (16450+ and some 8250s,
- special use with some boards)
-
-
- 80h 40h 20h 10h 08h 04h 02h 01h
- Register Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
- -------------------------------------------------------------------------------
- IER 0 0 0 0 EDSSI ELSI ETBEI ERBFI
- IIR (r/o) FIFO en FIFO en 0 0 IID2 IID1 IID0 pending
- FCR (w/o) - RX trigger - 0 0 DMA sel XFres RFres enable
- LCR DLAB SBR stick par even sel Par en stopbits - word length -
- MCR 0 0 AFE Loop OUT2 OUT1 RTS DTR
- LSR FIFOerr TEMT THRE Break FE PE OE RBF
- MSR DCD RI DSR CTS DDCD TERI DDSR DCTS
-
- EDSSI: Enable Delta Status Signals Interrupt
- ELSI: Enable Line Status Interrupt
- ETBEI: Enable Transmitter Buffer Empty Interrupt
- ERBFI: Enable Receiver Buffer Full Interrupt
- FIFO en: FIFO enable
- IID#: Interrupt IDentification
- pending: an interrupt is pending if '0'
- RX trigger: RX FIFO trigger level select
- DMA sel: DMA mode select
- XFres: Transmitter FIFO reset
- RFres: Receiver FIFO reset
- DLAB: Divisor Latch Access Bit
- SBR: Set BReak
- stick par: Stick Parity select
- even sel: Even Parity select
- stopbits: Stop bit select
- word length: Word length select
- FIFOerr: At least one error is pending in the RX FIFO chain
- TEMT: Transmitter Empty (last word has been sent)
- THRE: Transmitter Holding Register Empty (new data can be written to THR)
- Break: Broken line detected
- FE: Framing Error
- PE: Parity Error
- OE: Overrun Error
- RBF: Receiver Buffer Full (Data Available)
- DCD: Data Carrier Detect
- RI: Ring Indicator
- DSR: Data Set Ready
- CTS: Clear To Send
- DDCD: Delta Data Carrier Detect
- TERI: Trailing Edge Ring Indicator
- DDSR: Delta Data Set Ready
- DCTS: Delta Clear To Send
- AFE: Automatic Flow control Enable
-
-
- RBR (Receive Buffer Register) 3F8h 2F8h 3E8h 2E8h +0 r/o
- ------------------------------------------------------------------------------
-
- This is where you get received characters from. This register is read-only.
-
-
-
- THR (Transmitter Holding Register) 3F8h 2F8h 3E8h 2E8h +0 w/o
- ------------------------------------------------------------------------------
-
- Send characters by writing them to this register. It is write-only.
-
-
-
- IER (Interrupt Enable Register) 3F9h 2F9h 3E9h 2E9h +1 r/w
- ------------------------------------------------------------------------------
-
- Enable several interrupts by setting these bits:
- Bit 0: If set, DR (Data Ready) interrupt is enabled. It is generated
- if data waits to be read by the CPU.
- Bit 1: If set, THRE (THR Empty) interrupt is enabled. This interrupt
- tells the CPU to write characters to the THR.
- Bit 2: If set, Status interrupt is enabled. It informs the CPU of
- occurred transmission errors during reception.
- Bit 3: If set, Modem status interrupt is enabled. It is triggered
- whenever one of the delta-bits is set (see MSR).
- Bits 4-7 are not used and should be set 0.
-
-
-
- DL (Divisor Latch) 3F8h 2F8h 3E8h 2E8h +0 r/w
- ------------------------------------------------------------------------------
-
- To access this *WORD*, set DLAB in the LCR to 1. Then write a word (16 bits)
- to this register or write the lower byte to base+0 and the higher byte to
- base+1 (the order is not important) to program the bps rate as follows:
-
- xtal frequency in Hz / 16 / desired rate = divisor
- xtal frequency in Hz / 16 / divisor = obtained rate
-
- Your PC uses an xtal frequency of 1.8432 MHz (that's 1843200 Hz :-).
-
- Do *NOT* use 0 as a divisor (your maths teacher told you so)! It results in
- a rate of about 3500 bps, but it is not guaranteed to work with all chips in
- the same way.
-
- An error of up to 3-5 percent is irrelevant.
-
- Some values (1.8432 MHz quartz, as in the PC):
-
- bps rate Divisor (hex) Divisor (dec) Percent Error
- 50 900 2304 0.0%
- 75 600 1536 0.0%
- 110 417 1047 0.026%
- 134.5 359 857 0.058%
- 150 300 768 0.0%
- 300 180 384 0.0%
- 600 C0 192 0.0%
- 1200 60 96 0.0%
- 1800 40 64 0.0%
- 2000 3A 58 0.69%
- 2400 30 48 0.0%
- 3600 20 32 0.0%
- 4800 18 24 0.0%
- 7200 10 16 0.0%
- 9600 C 12 0.0%
- 19200 6 6 0.0%
- 38400 3 3 0.0%
- 57600 2 2 0.0%
- 115200 1 1 0.0%
-
- The 16450 is capable of up to 512 kbps according to NS.
-
- NS specifies that the 16550A is capable of 256 kbps if you use a 4 MHz
- or an 8 MHz crystal. But a staff member of NS Germany (I know that this
- abbreviation is not well-chosen :-( ) told one of my friends on the phone
- that it runs correctly at 512 kbps as well; I don't know if the 1488/1489
- manage this, though. This is true for the 16C552, too. See the "known
- problems" section.
-
- BTW: Ever tried 1.76 bps, the slowest rate possible? Kindergarten kids
- write faster.
-
- The Microsoft mouse uses 1200 bps, 7n1, the Mouse Systems mouse uses 1200
- bps, 8n1. See the Mouse chapter for details.
-
-
-
- IIR (Interrupt Identification Register) 3FAh 2FAh 3EAh 2EAh +2 r/o
- ------------------------------------------------------------------------------
-
- This register allows you to detect the cause of an interrupt. Only one
- interrupt is reported at a time; they are priorized. If an interrupt occurs,
- Bit 0 tells you if the UART has triggered it. Follow the information in this
- register, then test bit 0 again. If it is still not set, there is another
- interrupt to be serviced. BTW: If you AND the value of this register with
- 06h, you get a pointer to a table of four words... ideal for near calls.
- Another hint: make sure your software reads this register just once and then
- follows the information it got before it is read again, otherwise your code
- won't work. (Turbo Pascal "programmers" beware! :)
-
- The bits 6 and 7 allow you to detect if the FIFOs of the 16550+ have been
- activated.
-
-
- Bit 3 Bit 2 Bit 1 Bit 0 Priority Source Description
- 0 0 0 1 none no interrupt pending
- 0 1 1 0 highest Status OE, PE, FE or BI of the
- LSR set. Serviced by
- reading the LSR.
- 0 1 0 0 second Receiver DR or trigger level rea-
- ched. Serviced by read-
- ing RBR 'til under level
- 1 1 0 0 second FIFO No Receiver FIFO action
- since 4 words' time
- (neither in nor out) but
- data in RX-FIFO. Serviced
- by reading RBR.
- 0 0 1 0 third Transm. THRE. Serviced by read-
- ing IIR (if source of
- int only!) or writing
- to THR.
- 0 0 0 0 lowest Modem One of the delta flags
- in the MSR set. Serviced
- by reading MSR.
- Bit 6 & 7: 16550A: set if FCR bit 0 set.
- 16550: bit 7 set, bit 6 cleared if FCR bit 0 set.
- others: clear
- Other bits: clear (but don't rely on it; this is subject to change).
-
- In most software applications bits 3 to 7 should be masked when servicing
- the interrupt since they are not relevant. These bits cause trouble with
- old software relying on that they are cleared...
-
- NOTE! Even if some of these interrupts are disabled, the service routine
- can be confronted with *all* states shown above when the IIR is loop-polled
- until bit 0 is set (don't ask me why; it's just that I encontered this, and
- it's not much more work to play it safe). Check examples in the Programming
- section.
-
-
-
- FCR (FIFO Control Register) 3FAh 2FAh 3EAh 2EAh +2 w/o
- ------------------------------------------------------------------------------
-
- This register allows you to control the FIFOs of the 16550+. It does not exist
- on the 8250/16450.
-
- Bit 0: FIFO enable.
- Bit 1: Clear receiver FIFO. This bit is self-clearing.
- Bit 2: Clear transmitter FIFO. This bit is self-clearing.
- Bit 3: DMA mode (pins -RXRDY and -TXRDY), see below
- Bits 6-7: Trigger level of the DR-interrupt.
-
- Bit 7 Bit 6 Receiver FIFO trigger level
- 0 0 1
- 0 1 4
- 1 0 8
- 1 1 14
-
- Note: if bit 0 is cleared, all other bits are ignored.
-
- DMA mode operation is not available with your PC, but for the sake of
- completeness... here we go.
-
- If bit 3 is 0, DMA mode 0 is selected. The -RXRDY pin goes active-low
- whenever there is at least one character in the RX FIFO or in the RBR if
- the FIFO is disabled. -TXRDY goes active-low when the TX FIFO or the THR
- is empty. It goes high if one character is written to the THR (same as THRE,
- that's bit 5 of the LSR).
-
- If this bit is 1, DMA mode 1 is selected. The -RXRDY pin goes low if
- the trigger level of the RX FIFO is reached or if reception timed out
- (no characters received for a time that would have allowed to receive 4
- characters). -TXRDY goes low when the TX FIFO is empty. It goes high again
- if the FIFO is completely full. (Not that setting this bit to '1' would fix
- the weird behaviour of the THRE bit in FIFO mode operation, though). If the
- FIFOs are disabled, DMA mode 1 operates in the same way as DMA mode 0.
-
-
-
- LCR (Line Control Register) 3FBh 2FBh 3EBh 2EBh +3 r/w
- ------------------------------------------------------------------------------
-
- This register allows you to select the transmission protocol. It also contains
- the DLAB bit which switches the function of the addresses +0 and +1.
-
- Bit 1 Bit 0 word length Bit 2 Stop bits
- 0 0 5 bits 0 1
- 0 1 6 bits 1 1.5/2
- 1 0 7 bits (1.5 if word length is 5)
- 1 1 8 bits (1.5 does not work with some chips, see above)
-
- Bit 5 Bit 4 Bit 3 Parity type Bit 6 SOUT condition
- x x 0 no parity 0 normal operation
- 0 0 1 odd parity 1 forces TxD +12V (break)
- 0 1 1 even parity Bit 7 DLAB
- 1 0 1 mark parity 0 normal registers
- 1 1 1 space parity 1 divisor at reg 0, 1
-
- Mark parity: The parity bit is always '1' (the line is 'low').
- Space parity: The parity bit is always '0' (the line is 'high').
-
-
-
- MCR (Modem Control Register) 3FCh 2FCh 3ECh 2ECh +4 r/w
- ------------------------------------------------------------------------------
-
- This register allows to program some modem control lines and to switch to
- loopback mode.
-
- Bit 0: Programs -DTR. If set, -DTR is low and the DTR pin of the port
- goes 'high'.
- Bit 1: Programs -RTS. dito.
- Bit 2: Programs -OUT1. Normally not used in a PC, but used with some
- multi-port serial adapters to enable or disable a port. Best
- thing is to write a '1' to this bit.
- Bit 3: Programs -OUT2. If set to 1, interrupts generated by the UART
- are transferred to the ICU (Interrupt Control Unit) while 0
- sets the interrupt output of the card to high impedance.
- (This is PC-only).
- Bit 4: '1': local loopback. All outputs disabled. This is a means of
- testing the chip: you 'receive' all the data you send.
- Interrupts are fully operational in this mode.
- Bit 5: (Texas Instruments TL16C550C only, maybe some more; this
- is not a standard feature) '1': Enable automatic flow
- control. If RTS (bit 1) is '0', only auto-CTS is done, which
- means that no more characters are sent from the FIFO and
- no more Tx interrupts are generated as long as CTS is '0'.
- If RTS (bit 1) is '1', the RTS signal is dropped whenever the
- FIFO trigger level is reached. Note that if this bit is '1',
- delta CTS (see below) won't generate a modem status interrupt!
-
-
-
- LSR (Line Status Register) 3FDh 2FDh 3EDh 2EDh +5 r/w
- ------------------------------------------------------------------------------
-
- This register allows error detection and polled-mode operation.
-
- Bit 0 Data Ready (DR). Reset by reading RBR (but only if the RX FIFO is
- empty, 16550+).
- Bit 1 Overrun Error (OE). Reset by reading LSR. Indicates loss of data.
- Bit 2 Parity Error (PE). Indicates transmission error. Reset by LSR.
- Bit 3 Framing Error (FE). Indicates missing stop bit. Reset by LSR.
- Bit 4 Break Indicator (BI). Set if RxD is 'space' for more than 1 word
- ('break'). Reset by reading LSR.
- Bit 5 Transmitter Holding Register Empty (THRE). Indicates that a new
- word can be written to THR. Reset by writing THR. Note that this
- bit works in a weird way when FIFOs are enabled: it goes 0
- whenever there are characters in the TX-FIFO, not when the FIFO
- is full!
- Bit 6 Transmitter Empty (TEMT). Indicates that no transmission is
- running. Reset by reading LSR.
- Bit 7 (16550+ only) Set if at least one character in the RX FIFO has
- been received with an error. Cleared by reading LSR if there is
- no further error in the FIFO. Clear with all other chips.
-
-
-
- MSR (Modem Status Register) 3FEh 2FEh 3EEh 2EEh +6 r/w
- ------------------------------------------------------------------------------
-
- This register allows you to check several modem status lines. The delta bits
- are set if the corresponding signals have changed state since the last reading
- (except for TERI which is only set if -RI changed from active-low to
- inactive-high, that is if the RI line at the port changed from 'high' to
- 'low' and the phone stopped ringing).
-
- Bit 0: Delta CTS. Set if CTS has changed state since last reading.
- Bit 1: Delta DSR. Set if DSR has changed state since last reading.
- Bit 2: TERI. Set if -RI has changed from low to high (ie. RI at port
- has changed from +12V to -12V).
- Bit 3: Delta DCD. Set if DCD has changed state since last reading.
- Bit 4: CTS. 1 if 'high' at port.
- Bit 5: DSR. dito.
- Bit 6: RI. dito.
- Bit 7: DCD.
-
- In loopback mode (MCR bit 4 = 1), bit 4 shows the state of RTS (MCR bit 1),
- bit 5 shows the state of DTR (MCR bit 0), RI shows the state of OUT1 (MCR
- bit 2), and DCD shows the state of OUT2 (MCR bit 3). The delta registers
- act accordingly to the 'level transitions' of the data written to MCR.
- This is a good means of testing if a UART is present.
-
-
-
- SCR (Scratch Register) 3FFh 2FFh 3EFh 2EFh +7 r/w
- ------------------------------------------------------------------------------
-
- This is an all-purpose 8 bit store. NS recommends to store the value of the
- FCR (which is w/o) in this register for further use, but this is not
- mandatory and not recommended by me (see below). This register is only
- available with the 16450+; the standard 8250 doesn't have a scratch register
- (but then again some versions do).
-
- On some boards (especially RS-422/RS-485 boards), this register has a special
- meaning (enable receiver/transmitter drivers etc.), and with multi-port
- serial adapters it is often used to select the interrupt levels of the
- several ports and to determine which port has triggered interrupt. So you
- shouldn't use it for anything else in your programs.
-
-
-
- Excursion: Why and how to use the FIFOs (by Scott C. Sadow)
- -----------------------------------------------------------
-
- Normally when transmitting or receiving, the UART generates one
- interrupt for every character sent or received. For 2400 bps, typically
- this is 240/second. For 115,200 bps, this means 11,520/second. With FIFOs
- enabled, the number of interrupts is greatly reduced.
-
- A transmitter holding register empty interrupt is not generated until the
- FIFO is empty (last byte is being sent).
-
- So if you know it's a 16550A and the FIFOs are enabled, your TX interrupt
- routine can write up to 16 characters to the THR. Monitoring bit 5 (THRE) of
- the LSR is _no_good_ because this bit will be cleared immediately after your
- routine has written the first character to the THR! The chip gives you no
- feedback at all.
-
- Thus, the number of transmitter interrupts is reduced by a factor of 16.
- For 115,200 bps, this means only 720 interrupts per second. For receive
- data interrupts, the processing is similar to transmitter interrupts. The
- main difference is that the number of bytes in the FIFO (the trigger level)
- can be specified. When the trigger level is reached, a receive data
- interrupt is generated; any other data received is just put in the FIFO.
- The receive data interrupt is not cleared until the number of bytes in the
- FIFO is below the trigger level again.
-
- To add 16550A support to existing code, there are 2 requirements to be met:
-
- 1) When reading the IIR to determine the interrupt source, only
- use the lower 3 bits.
-
- 2) After the existing UART initialization code, try to enable the
- FIFOs by writing to the FCR. (A value of C7 hex will enable FIFO
- mode, clear both FIFOs, and set the receive trigger level at 14
- bytes). Next, read the IIR. If Bit 6 of the IIR is not set, the
- UART is not a 16550A, so write 0 to the FCR to disable FIFO mode.
-
-
- Multi-Port Serial Adapters
- --------------------------
-
- This is material I received from Mike Surikov.
-
- I want to give you some information on Multi-Serial adapters that
- provide four or eight asynchronous serial communication ports.
-
- Some of them have an Interrupt Vector (one for each four
- channels). The Interrupt Vector is used to enable/disable
- global interrupt and to detect which of the four channels is
- creating the interrupt (one IRQ is used for a group of four
- channels). Bit 7 of the Interrupt Vector is used to enable or
- disable ALL four channels by writing a logical 1 to enable or 0
- to disable interrupts. At the same time, each channel can be
- enabled or disabled separately by programming the OUT2 (and/or
- OUT1) signal in the 16450 chip.
-
- When you read the interrupt vector, you get an indication which
- port has triggered the interrupt, as it is shown below.
-
- [Since this may be different with each board, check your manual for
- details.]
-
- MSB LSB
- 7 6 5 4 3 2 1 0 <-- Interrupt Vector Register
- Channel 0 interrupt indicator (0-active)
- N/A Channel 1 interrupt indicator (0-active)
- Channel 2 interrupt indicator (0-active)
- Channel 3 interrupt indicator (0-active)
- Global interrupt: 1-enable; 0-disable
-
- For example, an 8 PORT RS-232C CARD can have the following
- configuration:
-
- Base IRQ Channel Interrupt
- Address Level Number Vector
-
- 2A0 7 0 2BF
- 2A8 7 1 2BF
- 2B0 7 2 2BF
- 2B8 7 3 2BF
- 1A0 5 0 1BF
- 1A8 5 1 1BF
- 1B0 5 2 1BF
- 1B8 5 3 1BF
-
- [The base addresses should be configurable by jumpers or DIP switches.]
-
- Note that the Interrupt Vector Registers overlap Scratch
- Registers, so the detect_UART routine must be changed for these
- boards. [See the Programming Section.]
-
-
-
- Some words about timing
- -----------------------
-
- The 8250 is a rather slow peripheral chip; it has a cycle delay for both
- reading and writing of 500nsec, which means that after every read or write
- access to any of the chip's registers the CPU has to wait at least 500nsec
- before reading or writing one of its registers again. Good thing that this
- chip is only used with some old XTs... the 8088/8086/V20/V30 family is slow
- enough for that.
-
- The 16450 and 16550A are rather fast; they need a delay of 125nsec after
- read access and 150nsec after write access before any other transfer.
- This means we only have a problem with these fancy new machines that allow
- cycle times of 50nsec and less. Luckily they add wait states to I/O bus
- accesses (wait states are additional cycles during which the bus does
- not change its state) or use a slower clock speed for I/O transfers (8 or
- 12 MHz). So if you have 12 MHz I/O clock speed and one wait state for I/O
- transfers, you don't have to worry.
-
- Some people believe in delaying I/O operations by adding NOPs or JMP $+2 to
- every I/O instruction (both do nothing but wasting time), but I don't think
- that's any good with a chip that needs stable data lines for at least
- 100nsec (so the CPU or the bus controller has to add a wait state anyway).
- You can always blame the hardware or the setup if your program doesn't work
- for timing reasons. :)
-
- However, there may be a problem with block instructions, esp. OUTSB. This
- instruction allows you to fill the Tx fifo of the 16550A rather fast (just
- 5 cycles per transfer on the 286, others take longer), but even a 25MHz 286
- takes 200nsec for each transfer, so this should be on the safe side, too.
- I don't use this instruction, but for other reasons than timing difficulties.
- It's just not very useful: it takes more time to make sure in advance that
- you don't overrun your buffer margins during an OUTSB than to check for
- the margins after every single transfer.
-
- Please note that all this relates to ISA boards. I don't have any experience
- with EISA or other fancy things like VLB!
-
-
-
- Handshaking
- -----------
-
- The method of exchanging signals for data flow control between computers
- and data sets is called handshaking. The most popular and most often used
- handshaking variant is called XON/XOFF; it's done by software, while other
- methods are hardware-based.
-
- XON/XOFF
-
- Two bytes that are not mapped to normal characters in the ASCII charset are
- called XON (DC1, Ctrl-Q, ASCII 17) and XOFF (DC3, Ctrl-S, ASCII 19).
- Whenever either one of the sides wants to interrupt the data flow from the
- other (eg. full buffers), it sends an XOFF ('Transmission Off'). When its
- buffers have been purged again, it sends an XON ('Transmission On') to
- signal that data can be sent again. (With some implementations, this can
- be any character).
-
- XON/XOFF is of course limited to text transmission. It cannot be used with
- binary data since binary files tend to contain every single one of the 256
- characters...
-
- That's why hardware handshaking is normally used with modems, while
- XON/XOFF is often used with printers and plotters and terminals.
-
- DTR/DSR
-
- The 'Data Terminal Ready' and 'Data Set Ready' signals of the serial port
- can be used for handshaking purposes, too. Their names express what they
- do: the computer signals with DTR that it is ready to send and receive data,
- while the data set sets DSR. With most modems, the meaning of these signals
- is slightly different: DTR is ignored or causes the modem to hang up if it
- is dropped, while DSR signals that a connection has been established.
-
- RTS/CTS
-
- While DTR and DSR are mostly used to establish a connection, RTS and CTS
- have been specially designed for data flow control. The computer signals
- with RTS ('Request To Send') that it wishes to send data to the data set,
- while the data set (modem) sets CTS ('Clear To Send') when it is ready to
- do one part of its job: to send data thru' the phone wires.
-
- A normal handshaking protocol between a computer and a modem looks like this:
-
-
- DTR ___--------------------------------------------------------------____
-
- DSR _____-------------------------------------------------------------___
-
- RTS ___________-----------------------_____----------------------________
-
- CTS ____________-------____------------_____----------------------_______
-
- (1)(2) (3)(4) (5) (6) (7)(8)(9)(10) (11)(12)(13)
-
- (1) The computer sets DTR to indicate that it wants to make use of the
- modem.
- (2) The modem signals that it is ready and that a connection has been
- established.
- (3) The computer requests permission to send.
- (4) The modem informs the computer that it is now ready to receive data from
- the computer and send it through the phone wires.
- (5) The modem drops CTS to signal to the computer that its internal buffers
- are full; the computer stops sending characters to the modem.
- (6) The buffers of the modem have been purged, so the computer may continue
- to send data.
- (7) This situation is not clear; either the computer's buffers are
- full and it wants to inform the modem of this, or it doesn't have any
- more data to be send to the modem. Normally, modems are configured to
- stop any transmission between the computer and the modem when RTS is
- dropped.
- (8) The modem acknowledges RTS cleared by dropping CTS.
- (9) RTS is again raised by the computer to re-establish data transmission.
- (10) The modem shows that it is ready to do its job.
- (11) No more data is to be sent.
- (12) The modem acknowledges this.
- (13) DTR is dropped by the computer; this causes most modems to hang up.
- After hang-up, the modem acknowledges with DSR low. If the connection
- breaks, the modem also drops DSR to inform the computer about it.
-
-
- BIOS API (Application Programs Interface)
- -----------------------------------------
-
- PC programs are meant to use the BIOS routines to program the UARTs.
- Even though this is *NOT RECOMMENDED* by me (awfully slow, limited and
- complicated), I give you the BIOS calls as specified by Big Blue. Call
- INT 14h with:
-
-
- AH=00h Serial port - Initialize
-
- AL: see table
- DX: Port number (0-3; 0 equ. 0x3f8, 1 equ. 0x2f8, etc., see Hardware)
-
- Bit 7 Bit 6 Bit 5 Rate [bps] Bit 4 Bit 3 Parity
- 1 1 1 9600 0 0 none
- 1 1 0 4800 1 0 none
- 1 0 1 2400 0 1 odd
- 1 0 0 1200 1 1 even
- 0 1 1 600
- 0 1 0 300 Bit 1 Bit 0 Data bits
- 0 0 1 150 0 0 5
- 0 0 0 110 0 1 6
- 1 0 7
- Bit 2 0 -> 1 stop bit, 1 -> 2 stop bits 1 1 8
-
- Returns:
- AH: RS-232C line status bits
- Bit
- 0: RBF - input data is available in buffer
- 1: OE - data has been lost
- 5: THRE - room is available in output buffer
- 6: TEMT - output buffer empty
- AL: Modem status bits
- 3: always 1
- 7: DCD - carrier detect
-
- AH=01h Serial port - Write character
-
- AL: character to be sent
- DX: Port
-
- Returns:
- AH: Bit 7 clear if successful, set if not. Bits 0-6 see INT 14h AH=03h
-
- AH=02h Serial port - Read character
-
- DX: Port
-
- Returns:
- AH: Line Status (see AH=03h)
- AL: Received character (if AH bit 7 is clear)
-
- Note:
- This routine times out if DSR is not asserted, even if data is
- available! (That's why you need the short wires from the "Connecting
- devices" chapter with some programs).
-
- AH=03h Serial port - Get port status
-
- DX: Port
-
- Returns:
- AH: Line Status
- Bit 7: Timeout
- Bit 6: TEMT Transmitter empty
- Bit 5: THRE Transmitter Holding Register Empty
- Bit 4: Break (broken line detected)
- Bit 3: FE Framing error
- Bit 2: PE Parity error
- Bit 1: OE Overrun error
- Bit 0: RDF Receiver buffer full (data available)
- AL: Modem Status
- Bit 7: DCD Carrier detect
- Bit 6: RI Ring indicator
- Bit 5: DSR Data set ready
- Bit 4: CTS Clear to send
- Bit 3: DDCD Delta carrier detect
- Bit 2: TERI Trailing edge of ring indicator
- Bit 1: DDSR Delta data set ready
- Bit 0: DCTS Delta Clear to send
-
-
- BIOS variables in the Data Segment at segment 40h:
-
- Offset Size Description
- 00h WORD Base I/O address of 1st serial I/O port, zero if none
- 02h WORD Base I/O address of 2nd serial I/O port, zero if none
- 04h WORD Base I/O address of 3rd serial I/O port, zero if none
- 06h WORD Base I/O address of 4th serial I/O port, zero if none
- Note: Above fields filled in turn by POST as it finds serial
- ports. POST never leaves gaps. DOS and BIOS serial device
- numbers may be redefined by re-assigning these fields.
- [POST: Power-On Self Test. CB]
- [Madis Kaal told me that there are BIOSes that leave gaps in the table,
- and I know of some that don't recognize COM4 correctly.]
-
-
- This information is sneaked from Ralf Brown's famous interrupt list (hope
- he doesn't mind). If you want more detailed facts on this interrupt, refer
- to this list. It's available from lots of FTP sites (choose one in your
- vicinity; it is *huge*).
-
-
-
- Mice
- ----
-
- The Microsoft Serial Mouse (or compatibles) is the device that is most often
- used with the Serial Port of the PC; it's the one with the two buttons. Mouse
- Systems compatible mice have three buttons. Here's some information I
- received from Stephen Warner and Angelo Haritsis:
-
- Pins Used:
- TxD, RTS and/or DTR are used as power sources for the mouse.
- RxD is used to receive data from the mouse.
-
- Mouse reset:
- Set UART to 'broken line' state (set bit 6 of the LCR) and clear the bits
- 0-1 of the MCR; wait a while and reverse the bits again.
-
- Serial transmission parameters:
- Microsoft Mouse 1200 bps, 7 data bits, 1 stop bit, no parity
- Mouse Systems Mouse 1200 bps, 8 data bits, 1 stop bit, no parity
-
- Data packet format of the Microsoft mouse:
- The data packet consists of 3 bytes. It is sent to the computer every time
- the mouse changes state (ie. the mouse is moved or the buttons are released/
- pressed).
-
- D6 D5 D4 D3 D2 D1 D0
-
- 1st byte 1 LB RB Y7 Y6 X7 X6
- 2nd byte 0 X5 X4 X3 X2 X1 X0
- 3rd byte 0 Y5 Y4 Y3 Y2 Y1 Y0
-
- The byte marked with 1 is sent first and then the others. The bit D6 in the
- first byte is used for synchronizing the software to the mouse packets
- if it goes out of sync.
-
- LB is the state of the left button (1 being the LB is pressed)
- RB is the state of the right button (1 being the RB is pressed)
- X0-7 movement of the mouse in the X direction since last packet (+ right)
- Y0-7 movement of the mouse in the Y direction since last packet (+ down )
-
- The Microsoft Mouse uses RTS as power source. Whenever RTS is set to '0'
- and reset to '1', the mouse performs an internal reset and sends the
- character 'M' to signal its presence. Three-button-mice send 'M3' if you
- drop and raise RTS (see above) in Microsoft mode; this is compatible
- with the Microsoft mouse driver and allows the firmware to check if it
- is really a three-button mouse.
- [Scott David Daniels received this info from Brian Onn]
-
-
- Data packet format of the Mouse Systems mouse:
- The data packet consists of 5 bytes.
-
- D7 D6 D5 D4 D3 D2 D1 D0
-
- 1st byte 1 0 0 0 0 LB MB RB
- 2nd byte X7 X6 X5 X4 X3 X2 X1 X0
- 3rd byte Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
- 4th byte equal to 2nd byte
- 5th byte equal to 3rd byte
-
- Bits 7-3 of the 1st byte are used for synchronization; it's rather
- improbable that they appear the same way in any of the other bytes.
-
- LB is the state of the left button (1 being the LB is pressed)
- MB is the state of the middle button (1 being the MB is pressed)
- RB is the state of the right button (1 being the RB is pressed)
- X0-7 movement of the mouse in the X direction since last packet (+ right)
- Y0-7 movement of the mouse in the Y direction since last packet (+ up )
-
- The mouse should rather be used with the mouse driver software; this
- ensures compatibility to future changes as well as bus mice and greatly
- reduces programming overhead. See Ralf Brown's interrupt list, interrupt 33h.
- It is available from lots of FTP sites (eg. garbo.uwasa.fi, /pc/programming),
- the files are called inter*.zip.
-
-
- Modems
- ======
-
- This chapter is rather brief for several reasons. I'm no modem expert at all
- and there exist better sources than this document if you want information on
- modems. Patrick Chen, the author of "The Joy of Telecomputing", has written
- such a file, and there's one available from Sergey Shulgin, too (I don't have
- their internet addresses). You can obtain these files from my archive;
- they are named "modem1" and "modem2".
-
-
- A modem (MOdualtor-DEModulator) is an interface between the serial port of
- your computer and the public telephone network. Modern modems are small
- computers of their own: they accept commands, do the dialing for you, buffer
- incoming data, perform data compression and such things. Several standards
- have been established (Bell, CCITT), and several "command languages" are in
- use, with the Hayes and Microcom commands being the most popular ones.
-
- Modems have two internal modes: the command mode and the data mode. After
- power-up, the modem is in the command mode, and this mode can be restalled
- by sending an 'escape sequence' (normally a pause of at least 1 second,
- then three '+' signs in one second, then a pause of at least 1 second).
-
- All I know about modems is some commands and some encoding schemes; I
- share this knowledge with you - please share yours with me!
-
-
- Encoding schemes
- ----------------
-
- I've sneaked this table from the posting 'FAQ zu /Z-NETZ/TELECOM/ALLGEMEIN'
- of Kristian Koehntopp <kris@black.toppoint.de> in 'de.newusers.questions'.
- He has copyrighted his posting, so please contact him if you wish to reproduce
- this information in any commercial way.
-
- These are the schemes recommended by CCITT (more than one speed means
- fallback/auto-retrain speeds):
-
- Transmission speed in bps Baud Modulation duplex usage
- --------------------------------------------------------------------
- V.17 14400 2400 TCM half FAX
- 12000, 9600, 7200 2400 TCM half FAX
- 4800 2400 QAM half FAX
- V.21 300 300 FSK full
- V.22 1200 600 DPSK full
- V.22bis 2400 600 QAM full
- V.23 1200/75 1200/75 FSK asymmetric BTX
- V.27ter 4800 1600 DPSK half FAX
- 2400 1200 DPSK half FAX
- V.29 9600 2400 QAM half FAX
- 7200 2400 QAM half FAX
- V.32 9600 2400 TCM/QAM full
- 4800 2400 QAM full
- V.32bis 14400 2400 TCM full
- 12000, 9600, 7200 2400 TCM full
- 4800 2400 QAM full
-
- FSK Frequency Shift Keying
- DPSK Differential Phase Shift Keying
- QAM Quadrature Amplitude Modulation
- TCM Trellis Coded Modulation
-
- Other V-Recommendations often heard of:
-
- V.24 - Meaning of the signals at the serial port.
- V.28 - Electrical levels (V.24, V.28, and ISO 2110 are equivlaent to EIA
- RS232)
- V.42 - Data protection method, not dependening on the modulation scheme
- in use.
- V.42bis - Compression scheme, also called BTLZ.
-
- Erich Smythe <esmythe@digex.net> posted a very informative and humorous
- article explaining different modulation schemes used with modems. You
- can find it in the FTP archive, named The_Serial_Port.more06.
-
-
- Hayes commands
- --------------
-
- Each command line starts with 'AT', then several commands, then carriage
- return.
-
- The list is not comprehensive at all; most modems have several commands of
- their own, but these commands are available with most modems:
-
- A/ Repeat last command (no prepending AT)
-
- A Take over phone line (if you've already picked up the phone).
-
- B Set communications standard.
- B0 - CCITT
- B1 - Bell
-
- C Switch carrier on/off.
- C0 - carrier off
- C1 - carrier on
-
- D Dial a number. Normally followed by
- T - tone dial
- P - pulse dial
- nothing - according to actual setting (see ATP/ATT)
- then a sequence of the follwing characters:
- 0-9 - the numbers to be dialed
- W - wait for dial tone
- , - wait 2 seconds
- @ - wait 5 seconds (?)
- ! - flash (put the phone on the hook for 1/2 second)
- > - earth key
- R - start connection right after dialing (eg. ATDPR equals ATA)
- If you just enter ATD, the modem takes over the line without dialing.
-
- E Echo on/off in the command mode
- E0 - no echo
- E1 - echo
-
- H Hang up
-
- L Volume control; followed by 0-3 (0 equ. lowest, 3 equ. highest volume)
-
- M Monitor
- M0 - Speaker off
- M1 - Speaker on while dialing and establishing a connection
- M2 - Speaker always on
- M3 - Speaker on while establishing a connection
-
- O Switch to data mode
- O0 - promptly
- O1 - with retrain (reduction of the data rate)
-
- P Pulse dial
-
- Q Responses to commands on/off
- Q0 - on
- Q1 - off
-
- S Set/read internal register, eg.
- S17=234 set reg. 17 to 234
- S17? read reg. 17
-
- T Tone dial
-
- V Verbose mode on/off
- V0 - short responses
- V1 - full responses
-
- X Phone tones recognition on/off
- X0 - Ignore busy sign, don't wait for dial tone, and just answer with
- "CONNECT" when a connection has been established (other settings
- produce more detailed messages)
- X1 - Ignore busy sign, don't wait for dial tone, but give full connect
- message
- X2 - Ignore busy sign but wait for dial tone
- X3 - Don't ignore busy sign, but don't wait for dial tone
- X4 - Don't ignore anything
-
- Y Break setting
- Y0 - Don't hang up when break signal is detected
- Y1 - Hang up when break is detected (&D2, &M0)
-
- Z Initialize modem
- Z - Default parameters
- Z0 - Setting 0
- Z1 - Setting 1
-
- &C DCD mode
- &C0 - always 1
- &C1 - DCD according to carrier
-
- &D DTR mode
- &D0 - ignore DTR
- &D1 - switch to command mode when DTR goes 0
- &D2 - hang up if DTR goes 0
- &D3 - initialize modem when DTR goes 0
-
- &F Set operation mode
- &F0 - according to Hayes, no data protocol
- &F1 - according to Microcom; MNP1-4 or MNP5 as specified by %C
- &F2 - according to Sierra; MNP1-4 or MNP5 as specified by %C
- &F3 - according to Sierra, V.42 or V.42bis as specified by %C
-
- These are the default settings:
- &F0 - B0, E1, L2, M1, P, Q0, V1, Y0, X1, &C1, &D0, &G0, &R0, &S0,
- S0=0, S1=0, S2=43, S3=13, S4=10, S5=8, S6=2, S7=30, S8=2,
- S9=6,S10=14, S11=75, S12=50, S14=AAh, S16=80h, S21=20h,
- S22=76h, S23=7, S25=5, S26=1, S27=40h
- &F1 - \A3, \C0, \E0, \G0, \K5, \N1, \Q0, \T0, \V0, \X0, %A0, %C1,
- %E1, %G0, &G1, S36=7h, S46=138h, S48=128h, S82=128h
- &F2 - \A3, \C2, \E0, \G1, \K5, \N3, \Q1, \T0, \V1, \X0, %A13, %C1,
- S36=7h, S46=138h, S48=128h, S82=128h
- &F3 - \A3, \C0, \E0, \G0, \K5, \N3, \O1, \T0, \V1, \X0, %A0, %C1,
- %E0, S36=7h, S46=138h, S48=7h, S82=128h
-
- &G Guard tone
- &G0 - off
- &G1 - 550 Hz
- &G2 - 1800 Hz
-
- &K Data flow control
- &K0 - none
- &K3 - bidirectional RTS/CTS handshaking
- &K4 - bidirectional XON/XOFF
- &K5 - unidirectional XON/XOFF
-
- &M Synchronous/asynchronous operation
- &M0 - asynchronous (the usual thing)
- &M1 - command mode asynchronous, data mode synchronous.
- &M2 - switch to synchronous mode, start dialing after DTR 0->1
- &M3 - switch to synchronous mode, don't dial
-
- &Q Further specification of the communication
- &Q0 to &Q3 - no V.42bis
- &Q5 - V.42bis
- &Q6 - V.42bis off, buffer data
-
- &R CTS mode
- &R0 - CTS follows RTS with the delay time of S26
- &R1 - CTS is 1 if the modem is in the data mode
-
- &S DSR mode
- &S0 - DSR always 1
- &S1 - according to CCITT V.24
-
- &T Test
- &T0 - normal operation (no test)
- &T1 - local analog loopback
- &T3 - local digital loopback
- &T4 - accept distant digital loopback
- &T5 - ignore distant digital loopback
- &T6 - start distant digital loopback
- &T7 - start distant digital loopback and self test
- &T8 - start distant analog loopback and self test
-
- &V Show modem status
-
- &Wn Save actual configuration (some modems only). Setting can be
- restored with ATZn. n normally ranges between 0 and 1.
- The following parameters are stored:
- B, C, E, L, M, P/T, Q, V, X, Y, &C, &D, &G, &R, &S, &T4/&T5,
- S0, S14, S18, S21, S22, S25, S26, S27
-
- &X Specify clock source for synchronous operation
- &X0 - modem generates clock
- &X1 - modem synchronizes with local clock
- &X2 - modem synchronizes with distant clock
-
- &Y Define default setting (see &W and Z)
- &Y0 - setting 0 is default
- &Y1 - setting 1 is default
-
- &Z Store phone number in diary
- &Zn=XXXXXX stores phone number XXXXXX under index n, where
- XXXXXX can be up to 30 digits and n ranges between 0 and 3.
-
-
- Microcom commands
- -----------------
-
- \A Set block length for MNP
- \A0 - 64 characters
- \A1 - 128 characters
- \A2 - 192 characters
- \A3 - 256 characters
-
- \Bn Send break signal for n times 100ms (MNP defaults to n=3).
-
- \C Set buffering
- \C0 - none at all
- \C1 - buffer data for 4 seconds as long as 200 characters aren't
- reached or as long as no MNP block is found
- \C2 - don't buffer. Switch back to normal operation after reception
- of the control character (fall-back, see %C)
-
- D/n Dial phone number n in the diary (see &Z)
-
- DL Redial last number
-
- \E Echo on/off in data mode
- \E0 - no echo
- \E1 - echo
-
- \G Data flow on/off (see \Q)
- \G0 - off
- \G1 - on
-
- \J Data rate adjust
- \J0 - the data rates computer-modem and modem-modem are independent
- \J1 - the data rate computer-modem follows the data rate modem-modem
-
- \Kn Break setting (don't know anything about this, just that it exists ;-)
-
- \N MNP select
- \N0 - standard mode, no MNP, data is buffered
- \N1 - direct mode, no MNP, no buffering
- \N2 - MNP, data is buffered
- \N3 - allow MNP on/off during connection, data is buffered
-
- \O Switch on MNP during connection (the rest of the line is being ignored!)
-
- \Pn Same as &Z
-
- \Q Set handshake (compare &K)
- \Q0 - no handshaking
- \Q1 - XON/XOFF
- \Q2 - modem controls data flow with CTS
- \Q3 - data flow control with RTS/CTS
-
- \S List complete configuration
-
- \Tn Set idle timer
- \T0 - timer off
- \Tnn - break connection after nn minutes without data exchange
- (1-90)
-
- \U Acknowledge MNP operation; rest of line is ignored!
-
- \V Verbose mode
- \V0 - messages according to Hayes, even if MNP (no \REL)
- \V1 - messages according to Microcom (\REL appended if MNP)
-
- \X Filter XON/XOFF characters
- \X0 - filter XOM/XOFF characters
- \X1 - don't filter them (the usual thing)
-
- \Y Same as AT\O\U with the difference that it is not necessary to
- first send AT\O to one modem and then AT\U to the other; just
- send AT\Y to each modem within 5 seconds
-
- %An Specify control character that provokes fallback from MNP to
- normal operation (see \C2). n=0..255 (ASCII code)
-
- %C MNP5
- %C0 - not allowed
- %C1 - allowed
-
- %E auto-retrain
- %E0 - no auto-retrain allowed
- %E1 - auto-retrain allowed according to CCITT
-
- %R Show all S registers
-
- %V Same as I3 (but don't ask me what it is ;-) Gives info on the firmware
- version with some modems.
-
-
- [... continued ...]
-
- --
- Chris Blum <chris@phil.uni-sb.de> http://www.phil.uni-sb.de/~chris/
-
- From chris@chris.telip.uni-sb.de Wed Mar 1 17:57:25 1995
- From: chris@chris.telip.uni-sb.de (Chris Blum)
- Newsgroups: comp.sys.ibm.pc.hardware.comm,comp.os.msdos.programmer
- Subject: The Serial Port rel. 18, part 3/3
- Date: 23 Feb 1995 00:24:27 +0100
- Organization: The Outside of the Asylum
- Reply-To: chris@phil.uni-sb.de (Chris Blum)
- NNTP-Posting-Host: chris.telip.uni-sb.de
- Keywords: comm, COM, UART, serial port
-
- [... continued ...]
-
-
- IRQ sharing - can it be done? (this applies to ISA bus systems only)
- -----------------------------
-
- Yes and no. Yes, it can be done in principle, and no, it can't be done
- by just configuring two ports to use the same interrupt.
-
- Let us first consider the hardware involved. PCs have ICUs (interrupt control
- units, or PICs - programmable interrupt controllers) of the 8259A type. They
- can be programmed to be triggered by a high signal level or a raising edge,
- which is already annoying because low level or falling edge would make add-on
- card design simpler. But to top this all off, they have internal pull-up
- resistors! Which means that if no card is using the interrupt, it is in
- the triggered state.
-
- How would cards share interrupts? They'd only be allowed to have their
- IRQ output in two states: active high or 'floating'. 'Floating' means the line
- is not driven at all, neither high nor low, it 'floats'. If all sharers of
- an interrupt line in the PC would only drive the line high or let it 'float',
- we'd have a simple interrupt sharing scheme (that would allow for even
- simpler design if the active state of the line was low) - if there wasn't
- this nasty internal pull-up resistor in the 8259A. <sarcasm on> Sadly IBM
- didn't provide an external pull-down resistor on the main board of the very
- first PC, so later designs could not have one either for compatibility's
- sake. <sarcasm off> 1.5kOhms would be a fine value; the 8259A produces 300uA
- that have to be sunk below 0.8v (so 2.6kOhms would be enough in theory,
- but having some safety margin can't hurt).
-
- So how can you have your ports sharing a common interrupt line? There are
- two approaches to this, each assuming you're familiar with using a soldering
- iron. What you must provide is a logical OR of all interrupt outputs that
- drive the line; while this can be done with an OR gate of course, it is far
- more practical to use some wired-OR facility. First you'll have to add the
- external pull-down resistor, either on the main board (where it really
- belongs) or on one of the cards. Use 1.5kOhms for this. Then cut the line
- between the card edge connector and the IRQ line driver (LS125) on each and
- every card. Do this carefully; if it's a multi-layer card, you'd better cut
- the pin of the LS125, or maybe you can just replace a jumper with a diode.
- Now solder a diode (1N4148 will do, slow power diodes won't) over the cut
- with the cathode (usually marked with a ring, but you'd better check that
- thoroughly if there are multiple rings; the 1N4148 normally has a yellow
- cathode ring) to the card edge connector. There you are! Now hardware will no
- longer be in the way of interrupt sharing. (A 'cleaner' solution would be to
- use a LS126 line driver instead of the diode with 'enable' connected to
- 'input', but that's only practical with from-scratch designs.)
-
- Now let's face the software problems. In theory, interrupt sharing works fine
- between different pieces of hardware, but practically this is limited to real
- operating systems that do all interrupt processing by themselves; MSDOS
- doesn't do that, so it's not a good option for PCs (even Linux users boot DOS
- sometimes, if only to play games). Sharing interrupts even between UARTs
- becomes problematic if there are several programs involved, eg. the mouse
- driver and some comm application; they'd have to know of each other. 'Daisy
- chaining' the interrupt (a program 'hooks' the interrupt by placing its
- handler's address in the IRQ serivce table and letting the handler call the
- address it found in that table at install time when it exits; no interrupt
- acknowledging is done by the handlers themselves, just by the stub handler at
- the end of the chain) doesn't work because DOS doesn't even provide a stub
- interrupt handler! So one of the programs would have to issue EOI (end of
- interrupt) to the ICU, but which one? How would it know it's the last one in
- the chain? Better forget daisy chaining interrupts under DOS if you want your
- programs to work reliably.
-
- The situation is much simpler if all UARTs sharing the same interrupt are
- used by the same program. This program has to be aware of the sharing
- mechanism, but programs that can make use of more than one serial port
- (especially libraries) usually are. Now there's only one problem to be
- solved: lock-up situations. As I already wrote, the ICUs in the PC are
- programmed to use raising edge trigger mode, and you can't change this
- without crashing the system. Now consider the following situation. Two
- UARTs share one IRQ line. UART #1 raises the line because it needs service;
- the service routine is called and detects that UART #1 needs service. Before
- it can perform the serivce, UART #2 raises the IRQ, too. Now UART #1 is
- serviced, the line should go to the 'low' state but it doesn't because of
- the other UART keeping it high; the handler checks the next UART in its
- table and sees that UART #2 needs service, too. Now UART #1 receives another
- character and keeps the line high while UART #2 is being serviced. How should
- the handler know that this has happened? If it just issued EOI and returned,
- the IRQ line would never have gone 'low' during the service, so there won't
- be any future raising edges to be detected, and thus no more interrupts!
-
- What does the service routine do to avoid lock-ups? It has to mask the
- interrupt in the ICU; this resets the edge detector. If it unmasks the
- interrupt again at the end of the handler and the line is still 'high',
- this will trigger the edge detector and the interrupt will be scheduled
- again. See the 'known problems' section for a very solid method of handling
- interrupts suggested by Richard Clayton.
-
- Windows allows for UARTs sharing interrupts; just make sure the COM ports
- are configured properly in the system setup.
-
- A note to Linux users: Linux is fully capable of sharing interrupts between
- serial ports if the hardware problems described above are solved. Using the
- same interrupt for several UARTs even reduces CPU load, so it is definitely a
- Good Thing as long as there are not too many sharers. Having a well-designed
- and kernel-supported multi-port card is even better because these cards
- provide a mechanism for the handler to detect which UART has triggered
- interrupt without having to look at every single IIR, which reduces overhead
- even further.
-
-
-
- Programming
- ===========
-
- Now for the clickety-clickety thing. I hope you're a bit keen in
- assembler programming. Programming the UART in high level languages is,
- of course, possible, but not at very high rates. I give you several
- routines in assembler and C that do the dirty work for you.
-
- If you're keen on examples of how to program the UART in high level
- languages, even interrupt-driven, you should have a look at some code
- I received from Frank Whaley (ftp: "The_Serial_Port.more04") and at
- the "Async Routines Library" Scott A. Deming is currently developing
- (ftp: "asyam.zip").
-
- First thing to do is detect which chip is used. It shouldn't be difficult
- to convert this C function into assembler; I'll omit the assembly version.
-
- int detect_UART(unsigned baseaddr)
- {
- // this function returns 0 if no UART is installed.
- // 1: 8250, 2: 16450 or 8250 with scratch reg., 3: 16550, 4: 16550A
- int x,olddata;
-
- // check if a UART is present anyway
- olddata=inp(baseaddr+4);
- outp(baseaddr+4,0x10);
- if ((inp(baseaddr+6)&0xf0)) return 0;
- outp(baseaddr+4,0x1f);
- if ((inp(baseaddr+6)&0xf0)!=0xf0) return 0;
- outp(baseaddr+4,olddata);
- // next thing to do is look for the scratch register
- olddata=inp(baseaddr+7);
- outp(baseaddr+7,0x55);
- if (inp(baseaddr+7)!=0x55) return 1;
- outp(baseaddr+7,0xAA);
- if (inp(baseaddr+7)!=0xAA) return 1;
- outp(baseaddr+7,olddata); // we don't need to restore it if it's not there
- // then check if there's a FIFO
- outp(baseaddr+2,1);
- x=inp(baseaddr+2);
- // some old-fashioned software relies on this!
- outp(baseaddr+2,0x0);
- if ((x&0x80)==0) return 2;
- if ((x&0x40)==0) return 3;
- return 4;
- }
-
- If it's not a 16550A, FIFO mode operation won't work, but there's no
- problem in switching it on nevertheless as long as no 16550 is used and
- your software is aware that there is no TX FIFO available (see below). If
- your software doesn't use the FIFOs explicitly, write 0x7 to the FCR and
- mask bits 3, 6 & 7 of the IIR. This does not reduce interrupt overhead but
- makes transmission more reliable without changing anything for the software.
- But remember that the 16550 has a bug with its FIFOs (see hardware section),
- so if the function above returns 3, switch the FIFOs off.
-
- Mike Surikov has provided me with an altered version of this function that
- works correctly with multi-port serial adapters, too. It's available from
- the ftp archive mentioned at the beginning. Look for the file
- "The_Serial_Port.more03".
-
- The prototype of this useful function has also been provided by Mike
- Surikov; I've rewritten it from scratch though. It allows you to detect which
- interrupt is used by a certain UART. There is an assembly version of Mike's
- version (which can only detect intlevels 0-7) of this function as well. It's
- available from the ftp archive as "The_Serial_Port.more02".
-
- int detect_IRQ(unsigned base)
- {
- // returns: -1 if no intlevel found, or intlevel 0-15
- char ier,mcr,imrm,imrs,maskm,masks,irqm,irqs;
-
- _asm cli; // disable all CPU interrupts
- ier = inp(base+1); // read IER
- outp(base+1,0); // disable all UART ints
- while (!(inp(base+5)&0x20)); // wait for the THR to be empty
- mcr = inp(base+4); // read MCR
- outp(base+4,0x0F); // connect UART to irq line
- imrm = inp(0x21); // read contents of master ICU mask register
- imrs = inp(0xA1); // read contents of slave ICU mask register
- outp(0xA0,0x0A); // next read access to 0xA0 reads out IRR
- outp(0x20,0x0A); // next read access to 0x20 reads out IRR
- outp(base+1,2); // let's generate interrupts...
- maskm = inp(0x20); // this clears all bits except for the one
- masks = inp(0xA0); // that corresponds to the int
- outp(base+1,0); // drop the int line
- maskm &= ~inp(0x20); // this clears all bits except for the one
- masks &= ~inp(0xA0); // that corresponds to the int
- outp(base+1,2); // and raise it again just to be sure...
- maskm &= inp(0x20); // this clears all bits except for the one
- masks &= inp(0xA0); // that corresponds to the int
- outp(0xA1,~masks); // now let us unmask this interrupt only
- outp(0x21,~maskm);
- outp(0xA0,0x0C); // enter polled mode; Mike Surikov reported
- outp(0x20,0x0C); // that order is important with Pentium/PCI systems
- irqs = inp(0xA0); // and accept the interrupt
- irqm = inp(0x20);
- inp(base+2); // reset transmitter interrupt in UART
- outp(base+4,mcr); // restore old value of MCR
- outp(base+1,ier); // restore old value of IER
- if (masks) outp(0xA0,0x20); // send an EOI to slave
- if (maskm) outp(0x20,0x20); // send an EOI to master
- outp(0x21,imrm); // restore old mask register contents
- outp(0xA1,imrs);
- _asm sti;
- if (irqs&0x80) // slave interrupt occured
- return (irqs&0x07)+8;
- if (irqm&0x80) // master interrupt occured
- return irqm&0x07;
- return -1;
- }
-
-
-
- Now the non-interrupt version of TX and RX.
-
- Let's assume the following constants are set correctly (either by
- 'CONSTANT EQU value' or by '#define CONSTANT value'). You can easily use
- variables instead, but I wanted to save the extra lines for the ADD
- commands then necessary... A cute trick for calculating I/O addresses in
- assembly programs is this: load an index register (BX, BP, SI, or DI)
- with the base address (and keep it there), then use LEA DX,[BX+offset]
- before each IN/OUT instead of MOV DX,base; ADD DX,offset. It saves you
- one or two cycles. :)
-
- UART_BASEADDR the base address of the UART
- UART_BAUDRATE the divisor value (eg. 12 for 9600 bps)
- UART_LCRVAL the value to be written to the LCR (eg. 0x1b for 8e1)
- UART_FCRVAL the value to be written to the FCR. Bit 0, 1 and 2 set,
- bits 6 & 7 according to trigger level wished (see above).
- 0x87 is a good value, 0x7 establishes compatibility
- (except that there are some bits to be masked in the IIR).
-
- First thing to do is initializing the UART. This works as follows:
-
- UART_init proc near
- push ax ; we are 'clean guys'
- push dx
- mov dx,UART_BASEADDR+3 ; LCR
- mov al,80h ; set DLAB
- out dx,al
- mov dx,UART_BASEADDR ; divisor
- mov ax,UART_BAUDRATE
- out dx,ax
- mov dx,UART_BASEADDR+3 ; LCR
- mov al,UART_LCRVAL ; params
- out dx,al
- mov dx,UART_BASEADDR+4 ; MCR
- xor ax,ax ; clear loopback
- out dx,al
- ;***
- pop dx
- pop ax
- ret
- UART_init endp
-
- void UART_init()
- {
- outp(UART_BASEADDR+3,0x80);
- outpw(UART_BASEADDR,UART_BAUDRATE);
- outp(UART_BASEADDR+3,UART_LCRVAL);
- outp(UART_BASEADDR+4,0);
- //***
- }
-
- If we wanted to use the FIFO functions of the 16550A, we'd have to add
- some lines to the routines above (where the ***s are).
- In assembler:
- mov dx,UART_BASEADDR+2 ; FCR
- mov al,UART_FCRVAL
- out dx,al
- And in C:
- outp(UART_BASEADDR+2,UART_FCRVAL);
-
- Don't forget to disable the FIFO when your program exits! Some other
- software may rely on this!
-
- Not very complex so far, isn't it? Well, I told you so at the very
- beginning, and I wanted to start easy. Now let's send a character.
-
- UART_send proc near
- ; character to be sent in AL
- push dx
- push ax
- mov dx,UART_BASEADDR+5
- us_wait:
- in al,dx ; wait until we are allowed to write a byte to the THR
- test al,20h
- jz us_wait
- pop ax
- mov dx,UART_BASEADDR
- out dx,al ; then write the byte
- pop dx
- ret
- UART_send endp
-
- void UART_send(char character)
- {
- while ((inp(UART_BASEADDR+5)&0x20)==0);
- outp(UART_BASEADDR,(int)character);
- }
-
- This one sends a null-terminated string.
-
- UART_send_string proc near
- ; DS:SI contains a pointer to the string to be sent.
- push si
- push ax
- push dx
- cld ; we want to read the string in its correct order
- uss_loop:
- lodsb
- or al,al ; last character sent?
- jz uss_end
- ;*1*
- mov dx,UART_BASEADDR+5
- push ax
- uss_wait:
- in al,dx
- test al,20h
- jz uss_wait
- mov dx,UART_BASEADDR
- pop ax
- out dx,al
- ;*2*
- jmp uss_loop
- uss_end:
- pop dx
- pop ax
- pop si
- ret
- UART_send_string endp
-
- void UART_send_string(char *string)
- {
- int i;
- for (i=0; string[i]!=0; i++)
- {
- //*1*
- while ((inp(UART_BASEADDR+5)&0x20)==0);
- outp(UART_BASEADDR,(int)string[i]);
- //*2*
- }
- }
-
- Of course we could have used our already programmed function/procedure
- UART_send instead of the piece of code limited by *1* and *2*, but we are
- interested in high-speed code and thus save the call/ret.
-
- It shouldn't be a hard nut for you to modify the above function/procedure
- so that it sends a block of data rather than a null-terminated string. I'll
- omit that here.
-
- Note that all these routines don't make any use of the TX FIFO! If we know
- for sure that it's a 16550A we're dealing with, and that its FIFOs are
- enabled, we could as well write up to 16 characters whenever bit 5 (THRE)
- of the LSR goes 1.
-
- Now for reception. We want to program routines that do the following:
- - check if a character has been received or an error occured
- - read a character if there's one available
-
- Both the C and the assembler routines return 0 (in AX) if there is
- neither an error condition nor a character available. If a character is
- available, Bit 8 is set and AL or the lower byte of the return value
- contains the character. Bit 9 is set if we lost data (overrun), bit 10
- signals a parity error, bit 11 signals a framing error, bit 12 shows if
- there is a break in the data stream and bit 15 signals if there are any
- errors in the FIFO (if we turned it on). The procedure/function is much
- smaller than this paragraph:
-
- UART_get_char proc near
- push dx
- mov dx,UART_BASEADDR+5
- in al,dx
- xchg al,ah
- and ax,9f00h
- test al,1
- jz ugc_nochar
- mov dx,UART_BASEADDR
- in al,dx
- ugc_nochar:
- pop dx
- ret
- UART_get_char endp
-
- unsigned UART_get_char()
- {
- unsigned x;
- x = (inp(UART_BASEADDR+5) & 0x9f) << 8;
- if (x&0x100) x|=((unsigned)inp(UART_BASEADDR))&0xff;
- return x;
- }
-
- This procedure/function lets us easily keep track of what's happening
- with the RxD pin. It does not provide any information on the modem status
- lines! We'll program that later on.
-
- If we wanted to show what's happening with the RxD pin, we'd just have to
- write a routine like the following (I use a macro in the assembler version
- to shorten the source code):
-
- DOS_print macro pointer
- ; prints a string in the code segment
- push ax
- push ds
- push dx
- push cs
- pop ds
- mov dx,pointer
- mov ah,9
- int 21h
- pop dx
- pop ds
- pop ax
- endm
-
- UART_watch_rxd proc near
- uwr_loop:
- ; check if keyboard hit; we want a possibility to break the loop
- mov ah,1 ; Beware! Don't call INT 16h with high transmission
- int 16h ; rates, it won't work!
- jnz uwr_exit
- call UART_get_char
- or ax,ax
- jz uwr_loop
- test ah,1 ; is there a character in AL?
- jz uwr_nodata
- push ax ; yes, print it
- mov dl,al ;\
- mov ah,2 ; better use this for high rates: mov ah,0eh
- int 21h ;/ int 10h
- pop ax
- uwr_nodata:
- test ah,0eh ; any error at all?
- jz uwr_loop ; this speeds up things since errors should be rare
- test ah,2 ; overrun error?
- jz uwr_noover
- DOS_print overrun_text
- uwr_noover:
- test ah,4 ; parity error?
- jz uwr_nopar
- DOS_print parity_text
- uwr_nopar:
- test ah,8 ; framing error?
- jz uwr_loop
- DOS_print framing_text
- jmp uwr_loop
- uwr_exit:
- ret
- overrun_text db "*** Overrun Error ***$"
- parity_text db "*** Parity Error ***$"
- framing_text db "*** Framing Error ***$"
- UART_watch_rxd endp
-
- void UART_watch_rxd()
- {
- union {
- unsigned val;
- char character;
- } x;
- while (!kbhit()) {
- x.val=UART_get_char();
- if (!x.val) continue; // nothing? Continue
- if (x.val&0x100) putc(x.character); // character? Print it
- if (!(x.val&0xe00)) continue; // any error condidion? No, continue
- if (x.val&0x200) printf("*** Overrun Error ***");
- if (x.val&0x400) printf("*** Parity Error ***");
- if (x.val&0x800) printf("*** Framing Error ***");
- }
- }
-
- The RX routines make use of the RX FIFO without any additional programming.
-
- If you call these routines from a function/procedure as shown below,
- you've got a small terminal program!
-
- terminal proc near
- ter_loop:
- call UART_watch_rxd ; watch line until a key is pressed
- xor ax,ax ; get that key from the keyboard buffer
- int 16h
- cmp al,27 ; is it ESC?
- jz ter_end ; yes, then end this function
- call UART_send ; send the character typed if it's not ESC
- jmp ter_loop ; don't forget to check if data comes in
- ter_end:
- ret
- terminal endp
-
- void terminal()
- {
- int key;
- while (1)
- {
- UART_watch_rxd();
- key=getche();
- if (key==27) break;
- UART_send((char)key);
- }
- }
-
- These, of course, should be called from an embedding routine like the
- following (the assembler routines concatenated will assemble as an .EXE
- file. Put the lines 'code segment' and 'assume cs:code,ss:stack' to the
- front).
-
- main proc near
- call UART_init
- call terminal
- mov ax,4c00h
- int 21h
- main endp
- code ends
- stack segment stack 'stack'
- dw 128 dup (?)
- stack ends
- end main
-
- void main()
- {
- UART_init();
- terminal();
- }
-
- Here we are. Now you've got everything you need to program simple
- polling UART software.
-
- You know the way. Go and add functions to check if a data set is there,
- then establish a connection. Don't know how? Set DTR, wait for DSR.
- If you want to send, set RTS and wait for CTS before you actually transmit
- data. You don't need to store old values of the MCR: this register is
- readable. Just read in the data, AND/OR the bits as required and write the
- byte back.
-
-
- Let us now write the interrupt-driven versions of the routines. This is going
- to be a bit voluminous, so I draw the scene and leave the painting to you. If
- you want to implement interrupt-driven routines in a C program use either the
- inline-assembler feature or link the objects together. Of course you can also
- program interrupts in C (or other languages for that matter (are there
- any? :)).
-
- You'll find a complete program using interrupts at the end of this chapter.
-
- First thing to do is initialize the UART the same way as shown above.
- But there is some more work to be done before you enable the UART
- interrupt: FIRST SET THE INTERRUPT VECTOR CORRECTLY! Use function 25h of
- the DOS interrupt 21h. Remember to store the old value (obtained by calling
- DOS interrupt 21h function 35h) and to restore this value when exiting
- to DOS again. See also the note on known bugs if you've got a 8250.
-
- UART_INT EQU 0Ch ; for COM2 / COM4 use 0bh
- UART_ONMASK EQU 11101111b ; for COM2 / COM4 use 11110111b
- UART_OFFMASK EQU NOT UART_ONMASK
- UART_IERVAL EQU ? ; replace ? by any value between 0h and 0fh
- ; (dependent on which ints you want)
- ; DON'T SET bit 1 now! (not with this kind of service
- ; routine, that is)
- UART_OLDVEC DD ?
-
- initialize_UART_interrupt proc near
- push ds
- push es ; first thing is to store the old interrupt
- push bx ; vector
- mov ax,3500h+UART_INT
- int 21h
- mov word ptr UART_OLDVEC,bx
- mov word ptr UART_OLDVEC+2,es
- pop bx
- pop es
- push cs ; build a pointer in DS:DX
- pop ds
- lea dx,interrupt_service_routine
- mov ax,2500h+UART_INT
- int 21h ; and ask DOS to set this pointer as the new interrrupt vector
- pop ds
- mov dx,UART_BASEADDR+4 ; MCR
- in al,dx
- or al,8 ; set OUT2 bit to enable interrupts
- out dx,al
- mov dx,UART_BASEADDR+1 ; IER
- mov al,UART_IERVAL ; enable the interrupts we want
- out dx,al
- in al,21h ; last thing to do is unmask the int in the ICU
- and al,UART_ONMASK
- out 21h,al
- sti ; and free interrupts if they have been disabled
- ret
- initialize_UART_interrupt endp
-
- deinitialize_UART_interrupt proc near
- push ds
- lds dx,UART_OLDVEC
- mov ax,2500h+UART_INT
- int 21h
- pop ds
- in al,21h ; mask the UART interrupt
- or al,UART_OFFMASK
- out 21h,al
- mov dx,UART_BASEADDR+1
- xor al,al
- out dx,al ; clear all interrupt enable bits
- mov dx,UART_BASEADDR+4
- out dx,al ; and disconnect the UART from the ICU
- ret
- deinitialize_UART_interrupt endp
-
- Now the interrupt service routine. It has to follow several rules:
- first, it MUST NOT change the contents of any register of the CPU! Then it
- has to tell the ICU (did I tell you that this is the interrupt control
- unit? It is also called PIC Programmable Interrupt Controller) that the
- interrupt is being serviced. Next thing is test which part of the UART needs
- service. Let's have a look at the following procedure:
-
- interupt_service_routine proc far ; define as near if you want to link .COM
- ;*1* ; it doesn't matter anyway since IRET is
- push ax ; always a FAR command
- push cx
- push dx
- push bx
- push sp
- push bp
- push si
- push di
- ;*2* replace the part between *1* and *2* by pusha on an 80186+ system
- push ds
- push es
- in al,21h
- or al,UART_OFFMASK
- out 21h,al
- mov al,20h ; remember: first thing to do in interrupt routines is tell
- out 20h,al ; the ICU about the service being done. This avoids lock-up
- int_loop:
- mov dx,UART_BASEADDR+2 ; IIR
- in al,dx ; check IIR info
- test al,1
- jnz int_end
- and ax,6 ; we're interested in bit 1 & 2 (see data sheet info)
- mov si,ax ; this is already an index! Well-devised, huh?
- call word ptr cs:int_servicetab[si] ; ensure a near call is used...
- jmp int_loop
- int_end:
- in al,21h
- and al,UART_ONMASK
- out 21h,al
- pop es
- pop ds
- ;*3*
- pop di
- pop si
- pop bp
- pop sp
- pop bx
- pop dx
- pop cx
- pop ax
- ;*4* *3* - *4* can be replaced by popa on an 80186+ based system
- iret
- interupt_service_routine endp
-
- This is the part of the service routine that does the decisions. Now we
- need four different service routines to cover all four interrupt source
- possibilities (EVEN IF WE DIDN'T ENABLE THEM! Let's play this safe).
-
- int_servicetab DW int_modem, int_tx, int_rx, int_status
-
- int_modem proc near
- mov dx,UART_BASE+6 ; MSR
- in al,dx
- ; do with the info what you like; probably just ignore it...
- ; but YOU MUST READ THE MSR or you'll lock up the interrupt!
- ret
- int_modem endp
-
- int_tx proc near
- ; get next byte of data from a buffer or something
- ; (remember to set the segment registers correctly!)
- ; and write it to the THR (offset 0)
- ; if no more data is to be sent, disable the THRE interrupt
- ; If the FIFOs are switched on (and you've made sure it's a 16550A!), you
- ; can write up to 16 characters
-
- ; end of data to be sent?
- ; no, jump to end_int_tx
- mov dx,UART_BASEADDR+1
- in al,dx
- and al,00001101b
- out dx,al
- end_int_tx:
- ret
- int_tx endp
-
- int_rx proc near
- mov dx,UART_BASEADDR
- in al,dx
- ; do with the character what you like (best write it to a
- ; FIFO buffer [not the one of the 16550A, silly! :)])
- ; the following lines speed up FIFO mode operation
- mov dx,UART_BASEADDR+5
- in al,dx
- test al,1
- jnz int_rx
- ; these lines are a cure for the well-known problem of TX interrupt
- ; lock-ups when receiving and transmitting at the same time
- test al,40h
- je dont_unlock
- call int_tx
- dont_unlock:
- ret
- int_rx endp
-
- int_status proc near
- mov dx,UART_BASEADDR+5
- in al,dx
- ; do what you like. It's just important to read the LSR
- ret
- int_status endp
-
- How is data sent now? Write it to a FIFO buffer (that's nothing to do with
- the built-in FIFOs of the 16550!) that is read by the interrupt routine.
- Then set bit 1 of the IER and check if this has already started transmission.
- If not, you'll have to start it by hand (just call the int_tx routine). THIS
- IS DUE TO THOSE NUTTY GUYS AT BIG BLUE WHO DECIDED TO USE EDGE TRIGGERED
- INTERRUPTS INSTEAD OF PROVIDING ONE SINGLE FLIP FLOP FOR THE 8253/8254!
- See the "Known Problems" section for another good method of handling the
- UART interrupts that avoids all these problems.
-
- This procedure can be a C function, too. It is not time-critical at all.
-
- ; copy data to buffer
-
- mov dx,UART_BASEADDR+1 ; IER
- in al,dx
- or al,2 ; set bit 1
- out dx,al
- nop
- nop ; give the UART some time to kick the interrupt...
- nop
- mov dx,UART_BASEADDR+5 ; LSR
- cli ; make sure no interrupts get in-between if not already running
- in al,dx
- test al,40h ; is there a transmission running?
- jz dont_crank ; yes, so don't mess it up
- call int_tx ; no, crank it up
- sti
- dont_crank:
-
- Well, that's it! Your main program has to take care of the buffers,
- nothing else!
-
- Remember to call deinitialize_UART_interrupt before exiting to DOS! In C,
- this can easily be done by adding the function to the at-exit list with
- the atexit() function. You won't have to worry about the myriads of ways
- your program could terminate then.
-
- For those of you who prefer learning by watching rather than learning by
- doing ("lazy" is such an ignorant word :-), here's the source of a
- small terminal program. It can be assembled with TASM or ML without
- any change. Wire together two PCs (three-wire-connection, see the
- beginning of this file) and start it on each of them. You can then
- type messages on both keyboards that can be viewed on both screens.
- If you press F1, a large string is being sent (but not displayed on
- the sender's screen). Ctrl-X terminates the program.
-
-
- ----8<--------8<--------8<--------8<--------8<--------8<--------8<----
-
- ; just a small terminal program using interrupts.
- ; It's quite dumb: it uses the BIOS for screen output
- ; and keyboard input
- ; assemble and link as .EXE (just type ml name)
- ; If you have a 16550 (not a 16550A), you may lose
- ; characters since the fifos are turned on (see "Known problems
- ; with several chips")
- ; If your BIOS locks the interrupts while scrolling (some do),
- ; you may encounter data loss at high rates.
-
- model small
- dosseg
-
- INTNUM equ 0Ch ; COM1; COM2: 0Bh
- OFFMASK equ 00010000b ; COM1; COM2: 00001000b
- ONMASK equ not OFFMASK
- UART_BASE equ 3F8h ; COM1; COM2: 2F8h
- UART_RATE equ 12 ; 9600 bps, see table in this file
- UART_PARAMS equ 00000011b ; 8n1, see tables
- RXFIFOSIZE equ 8096 ; set this to your needs
- TXFIFOSIZE equ 8096 ; dito.
- ; the fifos must be large on slow computers
- ; and can be small on fast ones
- ; These have nothing to do with the 16550A's
- ; built-in FIFOs!
-
- .data
- long_text db 0dh
- db "This is a very long test string. It serves the purpose of",0dh
- db "demonstrating that our interrupt-driven routines are capable",0dh
- db "of coping with pressure situations like the one we provoke",0dh
- db "by sending large bunches of characters in each direction at",0dh
- db "the same time. Run this test by pressing F1 at a low data",0dh
- db "rate and a high data rate to see why serial transmission and",0dh
- db "reception should be programmed interrupt-driven. You won't lose",0dh
- db "a single character as long as you don't overload the fifos, no",0dh
- db "matter how hard you try!",0dh,0
-
- ds_dgroup macro
- mov ax,DGROUP
- mov ds,ax
- assume ds:DGROUP
- endm
-
- ds_text macro
- push cs
- pop ds
- assume ds:_TEXT
- endm
-
- rx_checkwrap macro
- local rx_nowrap
- cmp si,offset rxfifo+RXFIFOSIZE
- jb rx_nowrap
- lea si,rxfifo
- rx_nowrap:
- endm
-
- tx_checkwrap macro
- local tx_nowrap
- cmp si,offset txfifo+TXFIFOSIZE
- jb tx_nowrap
- lea si,txfifo
- tx_nowrap:
- endm
-
- .stack 256
-
- .data?
- old_intptr dd ?
- rxhead dw ?
- rxtail dw ?
- txhead dw ?
- txtail dw ?
- bitxfifo dw 1 ; size of built-in TX fifo (1 if no fifo)
- rxfifo db RXFIFOSIZE dup (?)
- txfifo db TXFIFOSIZE dup (?)
-
- .code
- start proc far
- call install_interrupt_handler
- call clear_fifos
- call clear_screen
- call init_UART
- continue:
- call read_RX_fifo
- call read_keyboard
- jnc continue
- call clean_up
- mov ax,4c00h
- int 21h ; return to DOS
- start endp
-
- interrupt_handler proc far
- assume ds:nothing,es:nothing,ss:nothing,cs:_text
- push ax
- push cx
- push dx ; first save the regs we need to change
- push ds
- push si
- in al,21h
- or al,OFFMASK ; disarm the interrupt
- out 21h,al
- mov al,20h ; acknowledge interrupt
- out 20h,al
-
- ih_continue:
- mov dx,UART_BASE+2
- xor ax,ax
- in al,dx ; get interrupt cause
- test al,1 ; did the UART generate the int?
- jne ih_sep ; no, then it's somebody else's problem
- and al,6 ; mask bits not needed
- mov si,ax ; make a pointer out of it
- call interrupt_table[si] ; serve this int
- jmp ih_continue ; and look for more things to be done
- ih_sep:
-
- in al,21h
- and al,ONMASK ; rearm the interrupt
- out 21h,al
-
- pop si
- pop ds
- pop dx ; restore regs
- pop cx
- pop ax
- iret
- interrupt_table dw int_modem,int_tx,int_rx,int_status
- interrupt_handler endp
-
- int_modem proc near
- ; just clear modem status, we are not interested in it
- mov dx,UART_BASE+6
- in al,dx
- ret
- int_modem endp
-
- int_tx proc near
- ds_dgroup
- ; check if there's something to be sent
- mov si,txtail
- mov cx,bitxfifo
- itx_more:
- cmp si,txhead
- je itx_nothing
- cld
- lodsb
- mov dx,UART_BASE
- out dx,al ; write it to the THR
- ; check for wrap-around in our fifo
- tx_checkwrap
- ; send as much bytes as the chip can take when available
- loop itx_more
- jmp itx_dontstop
- itx_nothing:
- ; no more data in the fifo, so inhibit TX interrupts
- mov dx,UART_BASE+1
- mov al,00000001b
- out dx,al
- itx_dontstop:
- mov txtail,si
- ret
- int_tx endp
-
- int_rx proc near
- ds_dgroup
- mov si,rxhead
- irx_more:
- mov dx,UART_BASE
- in al,dx
- mov byte ptr [si],al
- inc si
- ; check for wrap-around
- rx_checkwrap
- ; see if there are more bytes to be read
- mov dx,UART_BASE+5
- in al,dx
- test al,1
- jne irx_more
- mov rxhead,si
- test al,40h ; Sometimes when sending and receiving at the
- jne int_tx ; same time, TX ints get lost. This is a cure.
- ret
- int_rx endp
-
- int_status proc near
- ; just clear the status ("this trivial task is left as an exercise
- ; to the student")
- mov dx,UART_BASE+5
- in al,dx
- ret
- int_status endp
-
- read_RX_fifo proc near
- ; see if there are bytes to be read from the fifo
- ; we read a maximum of 16 bytes, then return in order
- ; not to break keyboard control
- ds_dgroup
- cld
- mov cx,16
- mov si,rxtail
- rx_more:
- cmp si,rxhead
- je rx_nodata
- lodsb
- call output_char
- ; check for wrap-around
- rx_checkwrap
- loop rx_more
- rx_nodata:
- mov rxtail,si
- ret
- read_RX_fifo endp
-
- read_keyboard proc near
- ds_dgroup
- ; check for keys pressed
- mov ah,1
- int 16h
- je rk_nokey
- xor ax,ax
- int 16h
- cmp ax,2d18h ; is it Ctrl-X?
- stc
- je rk_ctrlx
- cmp ax,3b00h ; is it F1?
- jne rk_nf1
- lea si,long_text ; send a very long test string
- call send_string
- jmp rk_nokey
- rk_nf1:
- ; echo the character to the screen
- call output_char
-
- call send_char
- rk_nokey:
- clc
- rk_ctrlx:
- ret
- read_keyboard endp
-
-
- install_interrupt_handler proc near
- ds_dgroup
- ; install interrupt handler first
- mov ax,3500h+INTNUM
- int 21h
- mov word ptr old_intptr,bx
- mov word ptr old_intptr+2,es
- mov ax,2500h+INTNUM
- ds_text
- lea dx,interrupt_handler
- int 21h
- ret
- install_interrupt_handler endp
-
- clear_fifos proc near
- ds_dgroup
- ; clear fifos (not those in the 16550A, but ours)
- lea ax,rxfifo
- mov rxhead,ax
- mov rxtail,ax
- lea ax,txfifo
- mov txhead,ax
- mov txtail,ax
- ret
- clear_fifos endp
-
- init_UART proc near
- ; initialize the UART
- mov dx,UART_BASE+3
- mov al,80h
- out dx,al ; make DL register accessible
- mov dx,UART_BASE
- mov ax,UART_RATE
- out dx,ax ; write bps rate divisor
- mov dx,UART_BASE+3
- mov al,UART_PARAMS
- out dx,al ; write parameters
-
- ; is it a 16550A?
- mov dx,UART_BASE+2
- in al,dx
- and al,11000000b
- cmp al,11000000b
- jne iu_nofifos
- mov bitxfifo,16
- mov dx,UART_BASE+2
- mov al,11000111b
- out dx,al ; clear and enable the fifos if they exist
- iu_nofifos:
- mov dx,UART_BASE+1
- mov al,00000001b ; allow RX interrupts
- out dx,al
- mov dx,UART_BASE
- in al,dx ; clear receiver
- mov dx,UART_BASE+5
- in al,dx ; clear line status
- inc dx
- in al,dx ; clear modem status
- ; free interrupt in the ICU
- in al,21h
- and al,ONMASK
- out 21h,al
- ; and enable ints from the UART
- mov dx,UART_BASE+4
- mov al,00001000b
- out dx,al
- ret
- init_UART endp
-
- clear_screen proc near
- mov ah,0fh ; allow all kinds of video adapters to be used
- int 10h
- cmp al,7
- je cs_1
- mov al,3
- cs_1:
- xor ah,ah
- int 10h
- ret
- clear_screen endp
-
- clean_up proc near
- ds_dgroup
- ; lock int in the ICU
- in al,21h
- or al,OFFMASK
- out 21h,al
- xor ax,ax
- mov dx,UART_BASE+4 ; disconnect the UART from the int line
- out dx,al
- mov dx,UART_BASE+1 ; disable UART ints
- out dx,al
- mov dx,UART_BASE+2 ; disable the fifos (old software relies on it)
- out dx,al
- ; restore int vector
- lds dx,old_intptr
- mov ax,2500h+INTNUM
- int 21h
- ret
- clean_up endp
-
- output_char proc near
- push si
- push ax
- oc_cr:
- push ax
- mov ah,0eh ; output character using BIOS TTY
- int 10h ; it's your task to improve this
- pop ax
- cmp al,0dh ; add LF after CR; change it if you don't like it
- mov al,0ah
- je oc_cr
- pop ax
- pop si
- ret
- output_char endp
-
- send_char proc near
- push si
- push ax
- ds_dgroup
- pop ax
- mov si,txhead
- mov byte ptr [si],al
- inc si
- ; check for wrap-around
- tx_checkwrap
- mov txhead,si
- ; test if the interrupt is running at the moment
- mov dx,UART_BASE+5
- in al,dx
- test al,40h
- je sc_dontcrank
- ; crank it up
- ; note that this might not work with some very old 8250s
- mov dx,UART_BASE+1
- mov al,00000011b
- out dx,al
- sc_dontcrank:
- pop si
- ret
- send_char endp
-
- send_string proc near
- ; sends a null-terminated string pointed at by DS:SI
- ds_dgroup
- cld
- ss_more:
- lodsb
- or al,al
- je ss_end
- call send_char
- jmp ss_more
- ss_end:
- ret
- send_string endp
-
- end start
-
- ---->8-------->8-------->8-------->8-------->8-------->8-------->8----
-
- Stephen Warner provided me with an assembly source of a TSR program that
- puts every character it receives from the serial port in the keyboard
- buffer. This allows to remotely control nearly every other program; it works
- with ATs and higher computers only. I decided not to add it to this file
- since it doesn't show anything about programming the serial port that's
- not already covered by other listings in this file. If you are interested
- in it, you can obtain it from the ftp archive (it is named
- "The_Serial_Port.more01"). See the beginning of this file.
-
- One more thing: always remember that at 115,200 bps there is service to
- be done at least every 85 microseconds! On an XT with 4.77 MHz this is
- about 40 assembler commands! So forget about servicing the serial port at
- this rate in high-level languages on such computers. Using a 16550A is
- strongly recommended at high rates (turn on FIFOs) but not necessary
- with otherwise decent hardware.
-
- The interrupt service routines can be accelerated by not pushing that
- much registers, and pusha and popa are fast replacements for 8 other
- pushs/pops.
-
-
- Well, that's the end of my short :-) summary. Don't hesitate to correct
- me if I'm wrong (preferably via email) in the details (I hope not, but it's
- not easy to find typographical and other errors in a text that you've
- written yourself). And please help me to complete this file! If you've got
- anything to add, email it to me and I'll spread it round.
-
- I've received a lot of feedback from you, and I'd like to thank everybody
- who encouraged me to continue the work on this file.
-
-
- Yours
-
- Chris
-
- P.S. You surely have noticed that English isn't my native tongue... so please
- excuse everything that's not pleasant for the eye, or, even better, tell me
- about it! It shouldn't be an ordeal though, at least some have assured me
- so...
-
-
-
- --
- Chris Blum <chris@phil.uni-sb.de> http://www.phil.uni-sb.de/~chris/
-
-